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

Generate report with phys2bids outputs #243

Closed
wants to merge 112 commits into from
Closed
Show file tree
Hide file tree
Changes from 103 commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
bb7ec87
Initial commit
eurunuela Jun 16, 2020
45c1108
Merge pull request #1 from eurunuela/enh/report
62442katieb Jun 17, 2020
063365c
added dynamic plots and file tree to report
62442katieb Jun 18, 2020
cfca324
Add subtitle for the channel plots
62442katieb Jun 18, 2020
a0158d7
Merge pull request #3 from 62442katieb/report-enh
eurunuela Jun 18, 2020
37fd910
Fix merge conflict
eurunuela Jun 18, 2020
b90f4b3
Add phys2bids call to generate_report function
eurunuela Jun 18, 2020
a1a90f0
Fix spacing in directory tree
eurunuela Jun 18, 2020
67dac41
UI tweaks
eurunuela Jun 18, 2020
b870ac6
Add call to generate_report in phys2bids
eurunuela Jun 18, 2020
2d32980
Adds quality check button to move from quality check to log and back
eurunuela Jun 18, 2020
4fb76f7
Name changes and other little changes to the HTML
eurunuela Jun 18, 2020
0530c9a
Merge pull request #2 from eurunuela/enh/report
62442katieb Jun 18, 2020
7fe3b21
Merge branch 'master' of https://github.com/physiopy/phys2bids into e…
eurunuela Jun 18, 2020
bbe940b
Renaming of file and css edits
eurunuela Jun 18, 2020
354814e
Another change to css
eurunuela Jun 18, 2020
f07d5ba
Reduce top margin
eurunuela Jun 18, 2020
76fdc4e
Merge pull request #3 from eurunuela/enh/report
62442katieb Jun 18, 2020
a4daf6a
working on html report formatting
62442katieb Aug 22, 2020
57bfbc6
adjust CSS & report template for side-by-side plots and tree
62442katieb Aug 24, 2020
633e241
responsive layout in report for small screens
62442katieb Aug 24, 2020
0604ea9
Merge pull request #5 from 62442katieb/report-enh
eurunuela Aug 25, 2020
a07ee6e
Fix linting errors
eurunuela Aug 25, 2020
ad50188
Adjust call to generate report and fix merge conflicts
eurunuela Aug 25, 2020
84e7326
Add bokeh dependency
eurunuela Aug 25, 2020
0298836
Fix bug where keys were wrong when saving results to lists
eurunuela Aug 25, 2020
3105aef
removed debugging code from html_report.py
62442katieb Aug 25, 2020
114b0bf
Merge branch 'master' of https://github.com/physiopy/phys2bids into e…
eurunuela Oct 15, 2020
9ca18ff
Tweaked some CSS
eurunuela Oct 15, 2020
25cce5a
Report now shows entire time series
eurunuela Oct 15, 2020
d3f856f
Made logo a tiny bit smaller
eurunuela Oct 15, 2020
41720ff
Added text to ask for user's patience while loading the plots
eurunuela Oct 15, 2020
ae0aa87
Edited patience message
eurunuela Oct 15, 2020
4987a1a
Merge branch 'master' of https://github.com/physiopy/phys2bids into e…
eurunuela Oct 21, 2020
ae86b4f
Test saving report as artifact
eurunuela Oct 21, 2020
835b0a9
Second test on saving report
eurunuela Oct 21, 2020
5c386af
Test moving reports to reporting folder to get css and icons
eurunuela Oct 22, 2020
ed9c675
Remove destionation to see what files are saved
eurunuela Oct 22, 2020
34e8b72
Update phys2bids/tests/test_integration.py
eurunuela Oct 22, 2020
7027f85
Fix file copying bug
eurunuela Oct 23, 2020
37f43f4
Second try
eurunuela Oct 23, 2020
1ee4e4d
Add missing phy2bids folder
eurunuela Oct 23, 2020
6e278ce
Hopefully fixes report copying bug
eurunuela Oct 23, 2020
4932c7e
Definitely a bug fix
eurunuela Oct 23, 2020
1e163a8
Add report artifact to integrationtest_36
eurunuela Oct 23, 2020
1322d14
Add path to artifact_path
eurunuela Oct 23, 2020
0754104
Pulled from master
eurunuela Oct 24, 2020
ff10198
Reduce number of samples to plot on report
eurunuela Oct 26, 2020
4111ccb
Checking if CircleCI phys2bids version is the latest
eurunuela Oct 26, 2020
da08d0d
Update integration test
eurunuela Oct 28, 2020
3dec0b6
Update html_report
eurunuela Oct 28, 2020
f7752fe
Merge branch 'report-enh' into enh/report
62442katieb Oct 28, 2020
aad2c95
Merge pull request #4 from eurunuela/enh/report
62442katieb Oct 28, 2020
878dacc
clean up reporting
62442katieb Nov 12, 2020
e9d50d4
simplify downsampling to get to 100Hz
62442katieb Nov 12, 2020
2a7f652
except that the downsampling factor should be an integer
62442katieb Nov 12, 2020
ed75f4f
add import for new reporting data structure
62442katieb Nov 12, 2020
e92321b
remove errant print call
62442katieb Nov 12, 2020
b0ff292
Remove unused vars from phys2bids.py
62442katieb Nov 13, 2020
e215cfb
Remove errant return
62442katieb Nov 13, 2020
a69dc66
Update html_report.py for style
62442katieb Nov 13, 2020
989bb67
Merge pull request #6 from 62442katieb/report-enh
eurunuela Nov 13, 2020
3465c9c
Merge branch 'master' of https://github.com/physiopy/phys2bids into e…
eurunuela Nov 13, 2020
a376e75
Fix style error
eurunuela Nov 13, 2020
540448e
Fix integration test
eurunuela Nov 13, 2020
aa16ebc
Added init for reporting
eurunuela Nov 13, 2020
ac5975b
Fix path bug for windows
eurunuela Nov 13, 2020
49cfe10
Added Katie's changes and units to plots
eurunuela Nov 17, 2020
4b0e964
Fix style
eurunuela Nov 17, 2020
689127c
Fix path bug on windows
eurunuela Nov 17, 2020
a265152
Merge branch 'master' of https://github.com/physiopy/phys2bids into e…
eurunuela Nov 23, 2020
2aab8c9
Update docstrings
eurunuela Nov 23, 2020
0a73a80
Rename figsize variable
eurunuela Nov 23, 2020
05fccbe
Remove alias
eurunuela Nov 23, 2020
781a7ce
Made reports optional
eurunuela Nov 23, 2020
8137425
Add new argument to integration test so that the report is generated
eurunuela Nov 23, 2020
7f255be
Add doc libraries to tests
eurunuela Nov 23, 2020
be105ab
Rename argument to avoid having the same name as the function
eurunuela Nov 23, 2020
f73d0a1
Update call to phys2bids in tests accordingly
eurunuela Nov 23, 2020
a69a0a0
Update function call after variable renaming
eurunuela Nov 23, 2020
4f6b19f
Merge branch 'master' of https://github.com/physiopy/phys2bids into e…
eurunuela Nov 27, 2020
aef66db
Changed output path for reports and testing a thing on CI
eurunuela Nov 27, 2020
a7d13a6
Change path from which report files are copied
eurunuela Nov 27, 2020
f3e4816
Fix conflict
eurunuela Dec 9, 2020
93fb08a
Added close file that hopefully fixes the issue
eurunuela Dec 10, 2020
b44aad5
Changed report in integration test
eurunuela Dec 10, 2020
cb434a1
Remove breakpoint
eurunuela Dec 10, 2020
128019d
Trigger tests
eurunuela Dec 10, 2020
d826577
Add tsv extension
eurunuela Dec 10, 2020
e25a268
Edited removal of files
eurunuela Dec 10, 2020
e5fb476
Changed how files are removed
eurunuela Dec 10, 2020
0664a13
Merge branch 'master' of https://github.com/physiopy/phys2bids into e…
eurunuela Dec 14, 2020
4fafef2
Fix conflicts
eurunuela Feb 10, 2021
f1682b5
Update phys2bids/reporting/__init__.py
eurunuela Feb 15, 2021
e5cd581
Update phys2bids/reporting/html_report.py
eurunuela Feb 15, 2021
7c806fc
Update phys2bids/reporting/html_report.py
eurunuela Feb 15, 2021
dee7370
Update phys2bids/reporting/html_report.py
eurunuela Feb 15, 2021
bb04f38
Update phys2bids/reporting/html_report.py
eurunuela Feb 15, 2021
e3fc3b1
Update phys2bids/reporting/html_report.py
eurunuela Feb 15, 2021
4c9d861
Update phys2bids/reporting/html_report.py
eurunuela Feb 15, 2021
abc9e43
Update phys2bids/reporting/html_report.py
eurunuela Feb 15, 2021
13fa2f4
Fix conflicts
eurunuela Apr 16, 2021
8a3eee9
Assess review comments from Taylor
eurunuela Apr 16, 2021
02b175f
Update phys2bids/tests/test_integration.py
vinferrer Apr 21, 2021
5a05ae9
Update phys2bids/tests/test_integration.py
vinferrer Apr 21, 2021
147c084
Avoid removing test path
eurunuela Apr 21, 2021
1fa818f
Do not copy report to phys2bids folder.
eurunuela Apr 21, 2021
80017e1
Revert "Avoid removing test path"
eurunuela Apr 21, 2021
b588784
Do not remove testing files.
eurunuela Apr 21, 2021
4ae5c00
Change how version number from logger is checked.
eurunuela Apr 22, 2021
fb13e6c
Update checks for version number.
eurunuela Apr 22, 2021
dbdf365
Remove conversion path prior to running heuristics test to make sure …
eurunuela Apr 27, 2021
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
6 changes: 6 additions & 0 deletions phys2bids/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ def _get_parser():
help='full path to file with info needed to generate '
'participant.tsv file ',
default='')
optional.add_argument('-report', '--report',
dest='make_report',
action='store_true',
help='Generate a report with the data and generated folder structure. '
'Default is False.',
default=False)
optional.add_argument('-debug', '--debug',
dest='debug',
action='store_true',
Expand Down
8 changes: 7 additions & 1 deletion phys2bids/phys2bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from phys2bids import utils, viz, _version, bids
from phys2bids.cli.run import _get_parser
from phys2bids.physio_obj import BlueprintOutput
from phys2bids.reporting.html_report import generate_report
from phys2bids.slice4phys import slice4phys

from . import __version__
Expand Down Expand Up @@ -129,7 +130,8 @@ def print_json(outfile, samp_freq, time_offset, ch_name):
cite_module=True)
def phys2bids(filename, info=False, indir='.', outdir='.', heur_file=None,
sub=None, ses=None, chtrig=0, chsel=None, num_timepoints_expected=None,
tr=None, thr=None, pad=9, ch_name=[], yml='', debug=False, quiet=False):
tr=None, thr=None, pad=9, ch_name=[], yml='', make_report=False,
debug=False, quiet=False):
"""
Run main workflow of phys2bids.

Expand Down Expand Up @@ -439,6 +441,10 @@ def phys2bids(filename, info=False, indir='.', outdir='.', heur_file=None,
os.path.splitext(os.path.basename(phys_out[key].filename)
)[0]))

# Only generate report if specified by the user
if make_report:
generate_report(conversion_path, logname, phys_out[key])


def _main(argv=None):
options = _get_parser().parse_args(argv)
Expand Down
1 change: 1 addition & 0 deletions phys2bids/reporting/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Visual reporting tools for inspecting phys2bids workflow outputs."""
Binary file added phys2bids/reporting/assets/apple-icon-180x180.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
102 changes: 102 additions & 0 deletions phys2bids/reporting/assets/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
html, body {
margin: 0;
padding: 0;
font-family: 'Lato', sans-serif;
overflow-x: hidden;
overflow-y: scroll;
}
* {
box-sizing: border-box;
}

.header {
background: linear-gradient(90deg, rgba(0,240,141,1) 0%, rgba(0,73,133,1) 100%);
height: 70px;
width: 100%;
position: fixed;
overflow: hidden;
margin: 0;
z-index: 100;
}

.header a, span {
color: white;
text-decoration: none;
font-weight: 700;
}

.header_logo {
display: inline-block;
float: left;
}

.header_logo img{
height: 50px;
top: 0;
left: 0;
padding-top: 15px;
}

.header_links {
top: 0;
left: 0;
padding-top: 25px;
margin-left: 20px;
margin-right: 20px;
float: left;
display: inline-block;
}
.clear {
clear: both;
}

.content {
margin-top: 100px;
display: flex;
width: 100%;
}

.tree {
margin-left: 50px;
margin-right: 50px;
flex: 0.5;
min-width: 300px;
float: left;
}

.tree_text {
margin-top: 10px;
margin-bottom: 70px;
width: 100%;
}

.bk-root {
display: inline-block;
margin-top: 10px;
width: 100%;
}

.bokeh_plots {
margin-left: 50px;
margin-right: 50px;
flex: 1;
min-width: 500px;
float: left;
}

@media screen and (max-width: 600px) {
.content {
flex-wrap: wrap;
}
.tree {
flex-basis: 100%;
}
.bokeh_plots {
flex-basis: 100%;
}
}

.main{
margin-top: 100px;
margin-left: 100px;
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
234 changes: 234 additions & 0 deletions phys2bids/reporting/html_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
"""Reporting functionality for phys2bids."""
import sys
from distutils.dir_util import copy_tree
from os.path import join
from pathlib import Path
from string import Template
from bokeh.plotting import figure, ColumnDataSource
from bokeh.embed import components
from bokeh.layouts import gridplot

from phys2bids import _version


def _save_as_html(log_html_path, log_content, qc_html_path):
"""
Save an HTML report out to a file.

Parameters
----------
log_html_path : str
Body for HTML report with embedded figures
log_content: str
String containing the logs generated by phys2bids
qc_html_path : str
Path to the quality check section of the report

Returns
-------
html: HTML code of the report

Outcome
-------
Saves the html file
"""
resource_path = Path(__file__).resolve().parent
head_template_name = 'report_log_template.html'
head_template_path = resource_path.joinpath(head_template_name)
with open(str(head_template_path), 'r') as head_file:
head_tpl = Template(head_file.read())

html = head_tpl.substitute(version=_version.get_versions()['version'],
log_html_path=log_html_path, log_content=log_content,
qc_html_path=qc_html_path)
return html


def _update_fpage_template(tree_string, bokeh_id, bokeh_js, log_html_path, qc_html_path):
"""
Populate a report with content.

Parameters
----------
eurunuela marked this conversation as resolved.
Show resolved Hide resolved
tree_string: str
Tree of files in directory.
bokeh_id : str
HTML div created by bokeh.embed.components
bokeh_js : str
Javascript created by bokeh.embed.components
log_html_path : str
Path to the log section of the report
qc_html_path : str
Path to the quality check section of the report

Returns
eurunuela marked this conversation as resolved.
Show resolved Hide resolved
-------
body : Body for HTML report with embedded figures
"""
resource_path = Path(__file__).resolve().parent

body_template_name = 'report_plots_template.html'
body_template_path = resource_path.joinpath(body_template_name)
with open(str(body_template_path), 'r') as body_file:
body_tpl = Template(body_file.read())
body = body_tpl.substitute(tree=tree_string,
content=bokeh_id,
javascript=bokeh_js,
version=_version.get_versions()['version'],
log_html_path=log_html_path,
qc_html_path=qc_html_path)
return body


def _generate_file_tree(out_dir):
"""
eurunuela marked this conversation as resolved.
Show resolved Hide resolved
Populate a report with content.

Parameters
----------
outdir : str
Path to the output directory

Returns
eurunuela marked this conversation as resolved.
Show resolved Hide resolved
-------
tree_string: String with the tree of files in directory
"""
# prefix components:
space = ' '
branch = '│ '
# pointers:
tee = '├── '
last = '└── '

def tree(dir_path: Path, prefix: str = ''):
"""Generate tree structure.

Given a directory Path object
will yield a visual tree structure line by line
with each line prefixed by the same characters

from https://stackoverflow.com/questions/9727673/list-directory-tree-structure-in-python
"""
contents = list(dir_path.iterdir())
# contents each get pointers that are ├── with a final └── :
pointers = [tee] * (len(contents) - 1) + [last]
for pointer, path in zip(pointers, contents):
yield prefix + pointer + path.name
if path.is_dir(): # extend the prefix and recurse:
extension = branch if pointer == tee else space
# i.e. space because last, └── , above so no more |
yield from tree(path, prefix=prefix + extension)

tree_string = ''
for line in tree(Path(out_dir)):
tree_string += line + '<br>'
return tree_string


def _generate_bokeh_plots(phys_in, figsize=(250, 500)):
"""
Plot all the channels for visualizations as linked line plots for dynamic report.

Parameters
----------
phys_in: BlueprintInput object
Object returned by BlueprintInput class
figsize: tuple
eurunuela marked this conversation as resolved.
Show resolved Hide resolved
Size of the figure expressed as (size_x, size_y),
Default is 250x750px

Outcome
eurunuela marked this conversation as resolved.
Show resolved Hide resolved
-------
Creates new plot with path specified in outfile.

See Also
--------
https://phys2bids.readthedocs.io/en/latest/howto.html
"""
colors = ['#ff7a3c', '#008eba', '#ff96d3', '#3c376b', '#ffd439']

time = phys_in.timeseries.T[0] # assumes first phys_in.timeseries is time
ch_num = len(phys_in.ch_name)
if ch_num > len(colors):
colors *= 2

downsample = int(phys_in.freq / 100)
plot_list = []
for row, timeser in enumerate(phys_in.timeseries.T[1:]):
# build a data source for each plot, with only the data + index (time)
# for the purpose of reporting, data is downsampled 10x
# doesn't make much of a difference to the naked eye, fine for reports
source = ColumnDataSource(data=dict(
x=time[::downsample],
y=timeser[::downsample]))

i = row + 1

tools = ['wheel_zoom,pan,reset']
q = figure(plot_height=figsize[0], plot_width=figsize[1],
tools=tools,
title=f' Channel {i}: {phys_in.ch_name[i]}',
sizing_mode='stretch_both',
x_range=(0, 100))
q.line('x', 'y', color=colors[i - 1], alpha=0.9, source=source)
q.xaxis.axis_label = 'Time (s)'
# hovertool commented for posterity because I (KB) will be triumphant
# eventually
# q.add_tools(HoverTool(tooltips=[
# (phys_in.ch_name[i], '@y{0.000} ' + phys_in.units[i]),
# ('HELP', '100 :D')
# ], mode='vline'))
plot_list.append([q])
p = gridplot(plot_list, toolbar_location='right',
plot_height=250, plot_width=750,
merge_tools=True)
script, div = components(p)
return script, div


def generate_report(out_dir, log_path, phys_in):
"""
Plot all the channels for visualizations as linked line plots for dynamic report.

Parameters
----------
out_dir : str
File path to a completed phys2bids output directory
log_path: path
Path to the logged output of phys2bids
phys_in: BlueprintInput object
Object returned by BlueprintInput class

Outcome
eurunuela marked this conversation as resolved.
Show resolved Hide resolved
-------
Creates new plot with path specified in outfile.

See Also
--------
https://phys2bids.readthedocs.io/en/latest/howto.html
"""
# Copy assets into output folder
pkgdir = sys.modules['phys2bids'].__path__[0]
vinferrer marked this conversation as resolved.
Show resolved Hide resolved
assets_path = join(pkgdir, 'reporting', 'assets')
copy_tree(assets_path, join(out_dir, 'assets'))

# Read log
with open(log_path, 'r') as f:
log_content = f.read()

log_content = log_content.replace('\n', '<br>')
log_html_path = join(out_dir, 'phys2bids_report_log.html')
qc_html_path = join(out_dir, 'phys2bids_report.html')

html = _save_as_html(log_html_path, log_content, qc_html_path)

with open(log_html_path, 'wb') as f:
f.write(html.encode('utf-8'))

# Read in output directory structure & create tree
tree_string = _generate_file_tree(out_dir)
bokeh_js, bokeh_div = _generate_bokeh_plots(phys_in, figsize=(250, 750))
html = _update_fpage_template(tree_string, bokeh_div, bokeh_js, log_html_path, qc_html_path)

with open(qc_html_path, 'wb') as f:
f.write(html.encode('utf-8'))
Loading