From c1a7a447ceefcb99f8bbb6fdf72b049e179e3765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Uru=C3=B1uela?= Date: Thu, 24 Jun 2021 12:14:12 +0200 Subject: [PATCH 01/17] Add same changes that were on old branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Katie Bottenhorn Co-authored-by: Eneko Uruñuela Co-authored-by: Vicente Ferrer Co-authored-by: Taylor Salo Co-authored-by: Stefano Moia --- phys2bids/cli/run.py | 6 + phys2bids/phys2bids.py | 7 +- phys2bids/reporting/__init__.py | 1 + .../reporting/assets/apple-icon-180x180.png | Bin 0 -> 41458 bytes phys2bids/reporting/assets/main.css | 102 ++++++++ .../phys2bids_negativeReadTheDocs_500x150.png | Bin 0 -> 39877 bytes phys2bids/reporting/html_report.py | 234 ++++++++++++++++++ phys2bids/reporting/report_log_template.html | 33 +++ .../reporting/report_plots_template.html | 50 ++++ phys2bids/tests/test_integration.py | 78 +++--- setup.cfg | 5 + 11 files changed, 485 insertions(+), 31 deletions(-) create mode 100644 phys2bids/reporting/__init__.py create mode 100644 phys2bids/reporting/assets/apple-icon-180x180.png create mode 100644 phys2bids/reporting/assets/main.css create mode 100644 phys2bids/reporting/assets/phys2bids_negativeReadTheDocs_500x150.png create mode 100644 phys2bids/reporting/html_report.py create mode 100644 phys2bids/reporting/report_log_template.html create mode 100644 phys2bids/reporting/report_plots_template.html diff --git a/phys2bids/cli/run.py b/phys2bids/cli/run.py index 0527d6155..f2afa7e23 100644 --- a/phys2bids/cli/run.py +++ b/phys2bids/cli/run.py @@ -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', diff --git a/phys2bids/phys2bids.py b/phys2bids/phys2bids.py index f97323e74..8a95327d6 100644 --- a/phys2bids/phys2bids.py +++ b/phys2bids/phys2bids.py @@ -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__ @@ -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. @@ -438,6 +440,9 @@ def phys2bids(filename, info=False, indir='.', outdir='.', heur_file=None, os.path.join(conversion_path, 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): diff --git a/phys2bids/reporting/__init__.py b/phys2bids/reporting/__init__.py new file mode 100644 index 000000000..107d3aba2 --- /dev/null +++ b/phys2bids/reporting/__init__.py @@ -0,0 +1 @@ +"""Visual reporting tools for inspecting phys2bids workflow outputs.""" \ No newline at end of file diff --git a/phys2bids/reporting/assets/apple-icon-180x180.png b/phys2bids/reporting/assets/apple-icon-180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..4b6e2e4bfb2712a7a897aac95dc7ade0236d5a1c GIT binary patch literal 41458 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 literal 0 HcmV?d00001 diff --git a/phys2bids/reporting/html_report.py b/phys2bids/reporting/html_report.py new file mode 100644 index 000000000..12cde4a91 --- /dev/null +++ b/phys2bids/reporting/html_report.py @@ -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 + ---------- + 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 + ------- + 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): + """ + Populate a report with content. + + Parameters + ---------- + outdir : str + Path to the output directory + + Returns + ------- + 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 + '
' + 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 + Size of the figure expressed as (size_x, size_y), + Default is 250x750px + + Outcome + ------- + 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 + ------- + 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] + 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', '
') + 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')) \ No newline at end of file diff --git a/phys2bids/reporting/report_log_template.html b/phys2bids/reporting/report_log_template.html new file mode 100644 index 000000000..64182d820 --- /dev/null +++ b/phys2bids/reporting/report_log_template.html @@ -0,0 +1,33 @@ + + + + phys2bids report + + + + + +

+
+

$log_content

+
+ + \ No newline at end of file diff --git a/phys2bids/reporting/report_plots_template.html b/phys2bids/reporting/report_plots_template.html new file mode 100644 index 000000000..c3f56c9ca --- /dev/null +++ b/phys2bids/reporting/report_plots_template.html @@ -0,0 +1,50 @@ + + + + + phys2bids report + + + + + + + + +
+ + + + + +
+
+
+

Channel Plots

+

Hold on... It might take a few seconds to load the plots depending on how big your data is.

+ $content +
+
+

phys2BIDS Output Directory

+

$tree

+
+
+ + + +$javascript \ No newline at end of file diff --git a/phys2bids/tests/test_integration.py b/phys2bids/tests/test_integration.py index 6afc15215..e840e7df5 100644 --- a/phys2bids/tests/test_integration.py +++ b/phys2bids/tests/test_integration.py @@ -5,18 +5,23 @@ import shutil import subprocess from os import remove -from os.path import isfile, join, split -from pkg_resources import resource_filename +from os.path import dirname, isfile, join, split + +import phys2bids as p2b import pytest from phys2bids._version import get_versions from phys2bids.phys2bids import phys2bids +from pkg_resources import resource_filename def check_string(str_container, str_to_find, str_expected, is_num=True): - idx = [log_idx for log_idx, log_str in enumerate( - str_container) if str_to_find in log_str] + idx = [ + log_idx + for log_idx, log_str in enumerate(str_container) + if str_to_find in log_str + ] str_found = str_container[idx[0]] if is_num: num_found = re.findall(r"[-+]?\d*\.\d+|\d+", str_found) @@ -31,14 +36,20 @@ def test_integration_acq(skip_integration, samefreq_full_acq_file): """ if skip_integration: - pytest.skip('Skipping integration test') + pytest.skip("Skipping integration test") test_path, test_filename = split(samefreq_full_acq_file) test_chtrig = 3 - conversion_path = join(test_path, 'code', 'conversion') + conversion_path = join(test_path, "code", "conversion") - phys2bids(filename=test_filename, indir=test_path, outdir=test_path, - chtrig=test_chtrig, num_timepoints_expected=60, tr=1.5) + phys2bids( + filename=test_filename, + indir=test_path, + outdir=test_path, + chtrig=test_chtrig, + num_timepoints_expected=60, + tr=1.5, + ) # Check that files are generated for suffix in ['.json', '.tsv.gz']: @@ -68,17 +79,17 @@ def test_integration_acq(skip_integration, samefreq_full_acq_file): json_data = json.load(json_file) # Compares values in json file with ground truth - assert math.isclose(json_data['SamplingFrequency'], 10000.0) - assert math.isclose(json_data['StartTime'], 10.4251) - assert json_data['Columns'] == ['time', 'RESP - RSP100C', 'PULSE - Custom, DA100C', - 'MR TRIGGER - Custom, HLT100C - A 5', 'PPG100C', 'CO2', 'O2'] - - # Remove generated files - for filename in glob.glob(join(conversion_path, 'phys2bids*')): - remove(filename) - for filename in glob.glob(join(test_path, 'Test_belt_pulse_samefreq*')): - remove(filename) - shutil.rmtree(conversion_path) + assert math.isclose(json_data["SamplingFrequency"], 10000.0) + assert math.isclose(json_data["StartTime"], 10.4251) + assert json_data["Columns"] == [ + "time", + "RESP - RSP100C", + "PULSE - Custom, DA100C", + "MR TRIGGER - Custom, HLT100C - A 5", + "PPG100C", + "CO2", + "O2", + ] def test_integration_heuristic(skip_integration, multifreq_lab_file): @@ -100,21 +111,25 @@ def test_integration_heuristic(skip_integration, multifreq_lab_file): heur_path = resource_filename('phys2bids', 'heuristics') test_heur = join(heur_path, 'heur_test_multifreq.py') + shutil.rmtree(conversion_path) + # Move into folder subprocess.run(f'cd {test_path}', shell=True, check=True) # Phys2bids call through terminal - command_str = (f'phys2bids -in {test_full_path} ', - f'-chtrig {test_chtrig} -outdir {test_outdir} ', - f'-tr {test_tr} -ntp {test_ntp} -thr {test_thr} ', - f'-sub 006 -ses 01 -heur {test_heur}') - command_str = ''.join(command_str) + command_str = ( + f"phys2bids -in {test_full_path} ", + f"-chtrig {test_chtrig} -outdir {test_outdir} ", + f"-tr {test_tr} -ntp {test_ntp} -thr {test_thr} ", + f"-sub 006 -ses 01 -heur {test_heur} -report", + ) + command_str = "".join(command_str) subprocess.run(command_str, shell=True, check=True) # Check that call.sh is generated assert isfile(join(conversion_path, 'call.sh')) # Read logger file - logger_file = glob.glob(join(conversion_path, '*phys2bids*'))[0] + logger_file = glob.glob(join(conversion_path, "*phys2bids*.tsv"))[0] with open(logger_file) as logger_info: logger_info = logger_info.readlines() @@ -197,11 +212,14 @@ def test_integration_heuristic(skip_integration, multifreq_lab_file): assert math.isclose(json_data['StartTime'], 3.6960,) assert json_data['Columns'] == ['time', 'Trigger', 'CO2'] - # Remove generated files - shutil.rmtree(test_path_output) - shutil.rmtree(conversion_path) - for filename in glob.glob(join(test_path, 'Test1_multifreq_onescan*')): - remove(filename) + # shutil.copy( + # join(conversion_path, "phys2bids_report.html"), + # join(dirname(p2b.__file__), "reporting", "phys2bids_report.html"), + # ) + # shutil.copy( + # join(conversion_path, "phys2bids_report_log.html"), + # join(dirname(p2b.__file__), "reporting", "phys2bids_report_log.html"), + # ) def test_integration_multirun(skip_integration, multi_run_file): diff --git a/setup.cfg b/setup.cfg index cafcfd943..b47cf026a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -43,6 +43,8 @@ doc = sphinx >=2.0 sphinx-argparse sphinx_rtd_theme +reports = + bokeh style = flake8 >=3.7 flake8-docstrings >=1.5 @@ -52,12 +54,15 @@ interfaces = test = pytest >=5.3 pytest-cov + %(doc)s %(interfaces)s + %(reports)s %(style)s all = %(doc)s %(duecredit)s %(interfaces)s + %(reports)s %(style)s %(test)s From b58727b258c515ae72644871f87f277a69379148 Mon Sep 17 00:00:00 2001 From: "Marie-Eve Picard (she/her)" <77584086+me-pic@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:17:40 -0400 Subject: [PATCH 02/17] Fix bokeh version --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index b47cf026a..6bb991df1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,7 +44,7 @@ doc = sphinx-argparse sphinx_rtd_theme reports = - bokeh + bokeh ==2.4.3 style = flake8 >=3.7 flake8-docstrings >=1.5 From ad26feb4aee5ae5aad48be814609a05c737e386c Mon Sep 17 00:00:00 2001 From: "Marie-Eve Picard (she/her)" <77584086+me-pic@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:13:15 -0400 Subject: [PATCH 03/17] Fix bokehJS version to match bokeh version --- phys2bids/reporting/report_plots_template.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phys2bids/reporting/report_plots_template.html b/phys2bids/reporting/report_plots_template.html index c3f56c9ca..0c3ff92ad 100644 --- a/phys2bids/reporting/report_plots_template.html +++ b/phys2bids/reporting/report_plots_template.html @@ -3,11 +3,11 @@ phys2bids report - - - From 74776470b056e376c657c0222a520f266d41b32b Mon Sep 17 00:00:00 2001 From: "Marie-Eve Picard (she/her)" <77584086+me-pic@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:15:33 -0400 Subject: [PATCH 04/17] Fix x axes --- phys2bids/reporting/html_report.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/phys2bids/reporting/html_report.py b/phys2bids/reporting/html_report.py index 12cde4a91..698325aeb 100644 --- a/phys2bids/reporting/html_report.py +++ b/phys2bids/reporting/html_report.py @@ -168,8 +168,7 @@ def _generate_bokeh_plots(phys_in, figsize=(250, 500)): 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)) + sizing_mode='stretch_both') 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 From fd8beab54d3b2cb0001ee4e55ca7aa11878eca52 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 20:36:48 +0000 Subject: [PATCH 05/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- phys2bids/cli/run.py | 5 +- phys2bids/reporting/__init__.py | 2 +- phys2bids/reporting/assets/main.css | 2 +- phys2bids/reporting/html_report.py | 99 ++++++++++--------- phys2bids/reporting/report_log_template.html | 2 +- .../reporting/report_plots_template.html | 2 +- 6 files changed, 60 insertions(+), 52 deletions(-) diff --git a/phys2bids/cli/run.py b/phys2bids/cli/run.py index 0eb6cdf46..50da14e5a 100644 --- a/phys2bids/cli/run.py +++ b/phys2bids/cli/run.py @@ -190,7 +190,8 @@ def _get_parser(): default=False, ) optional.add_argument( - "-report", "--report", + "-report", + "--report", dest="make_report", action="store_true", help="Generate a report with the data and generated folder structure. " @@ -198,7 +199,7 @@ def _get_parser(): default=False, ) optional.add_argument("-v", "--version", action="version", version=("%(prog)s " + __version__)) - + parser._action_groups.append(optional) return parser diff --git a/phys2bids/reporting/__init__.py b/phys2bids/reporting/__init__.py index 107d3aba2..adeeb7218 100644 --- a/phys2bids/reporting/__init__.py +++ b/phys2bids/reporting/__init__.py @@ -1 +1 @@ -"""Visual reporting tools for inspecting phys2bids workflow outputs.""" \ No newline at end of file +"""Visual reporting tools for inspecting phys2bids workflow outputs.""" diff --git a/phys2bids/reporting/assets/main.css b/phys2bids/reporting/assets/main.css index 426336395..a5b9886bb 100644 --- a/phys2bids/reporting/assets/main.css +++ b/phys2bids/reporting/assets/main.css @@ -99,4 +99,4 @@ html, body { .main{ margin-top: 100px; margin-left: 100px; -} \ No newline at end of file +} diff --git a/phys2bids/reporting/html_report.py b/phys2bids/reporting/html_report.py index 698325aeb..49dd4f251 100644 --- a/phys2bids/reporting/html_report.py +++ b/phys2bids/reporting/html_report.py @@ -4,9 +4,10 @@ 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 bokeh.plotting import ColumnDataSource, figure from phys2bids import _version @@ -33,14 +34,17 @@ def _save_as_html(log_html_path, log_content, qc_html_path): Saves the html file """ resource_path = Path(__file__).resolve().parent - head_template_name = 'report_log_template.html' + 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: + 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) + 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 @@ -67,16 +71,18 @@ def _update_fpage_template(tree_string, bokeh_id, bokeh_js, log_html_path, qc_ht """ resource_path = Path(__file__).resolve().parent - body_template_name = 'report_plots_template.html' + 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: + 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) + 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 @@ -94,13 +100,13 @@ def _generate_file_tree(out_dir): tree_string: String with the tree of files in directory """ # prefix components: - space = ' ' - branch = '│ ' + space = " " + branch = "│ " # pointers: - tee = '├── ' - last = '└── ' + tee = "├── " + last = "└── " - def tree(dir_path: Path, prefix: str = ''): + def tree(dir_path: Path, prefix: str = ""): """Generate tree structure. Given a directory Path object @@ -119,9 +125,9 @@ def tree(dir_path: Path, prefix: str = ''): # i.e. space because last, └── , above so no more | yield from tree(path, prefix=prefix + extension) - tree_string = '' + tree_string = "" for line in tree(Path(out_dir)): - tree_string += line + '
' + tree_string += line + "
" return tree_string @@ -145,7 +151,7 @@ def _generate_bokeh_plots(phys_in, figsize=(250, 500)): -------- https://phys2bids.readthedocs.io/en/latest/howto.html """ - colors = ['#ff7a3c', '#008eba', '#ff96d3', '#3c376b', '#ffd439'] + 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) @@ -158,19 +164,20 @@ def _generate_bokeh_plots(phys_in, figsize=(250, 500)): # 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])) + 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') - q.line('x', 'y', color=colors[i - 1], alpha=0.9, source=source) - q.xaxis.axis_label = 'Time (s)' + 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", + ) + 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=[ @@ -178,9 +185,9 @@ def _generate_bokeh_plots(phys_in, figsize=(250, 500)): # ('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) + p = gridplot( + plot_list, toolbar_location="right", plot_height=250, plot_width=750, merge_tools=True + ) script, div = components(p) return script, div @@ -207,27 +214,27 @@ def generate_report(out_dir, log_path, phys_in): https://phys2bids.readthedocs.io/en/latest/howto.html """ # Copy assets into output folder - pkgdir = sys.modules['phys2bids'].__path__[0] - assets_path = join(pkgdir, 'reporting', 'assets') - copy_tree(assets_path, join(out_dir, 'assets')) + pkgdir = sys.modules["phys2bids"].__path__[0] + assets_path = join(pkgdir, "reporting", "assets") + copy_tree(assets_path, join(out_dir, "assets")) # Read log - with open(log_path, 'r') as f: + with open(log_path, "r") as f: log_content = f.read() - log_content = log_content.replace('\n', '
') - log_html_path = join(out_dir, 'phys2bids_report_log.html') - qc_html_path = join(out_dir, 'phys2bids_report.html') + log_content = log_content.replace("\n", "
") + 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')) + 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')) \ No newline at end of file + with open(qc_html_path, "wb") as f: + f.write(html.encode("utf-8")) diff --git a/phys2bids/reporting/report_log_template.html b/phys2bids/reporting/report_log_template.html index 64182d820..fb1cfb32f 100644 --- a/phys2bids/reporting/report_log_template.html +++ b/phys2bids/reporting/report_log_template.html @@ -30,4 +30,4 @@

$log_content

- \ No newline at end of file + diff --git a/phys2bids/reporting/report_plots_template.html b/phys2bids/reporting/report_plots_template.html index 0c3ff92ad..fef38570e 100644 --- a/phys2bids/reporting/report_plots_template.html +++ b/phys2bids/reporting/report_plots_template.html @@ -47,4 +47,4 @@

phys2BIDS Output Directory

-$javascript \ No newline at end of file +$javascript From f5473b53a0c8426a86f5131e7def20fb4238feaa Mon Sep 17 00:00:00 2001 From: "Marie-Eve Picard (she/her)" <77584086+me-pic@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:20:14 -0400 Subject: [PATCH 06/17] Trying to fix doc --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 16e349c36..22eacc758 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ copyright = "2019-{}, {}".format(datetime.now().year, author) # Import project to get version info -sys.path.insert(0, os.path.abspath(os.path.pardir)) +sys.path.insert(0, os.path.abspath("../")) import phys2bids # noqa # The short X.Y version From 10ab4efa159d45dfaaca0cb40bc7e51e1c11a098 Mon Sep 17 00:00:00 2001 From: "Marie-Eve Picard (she/her)" <77584086+me-pic@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:29:59 -0400 Subject: [PATCH 07/17] Revert "Trying to fix doc" This reverts commit f5473b53a0c8426a86f5131e7def20fb4238feaa. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 22eacc758..16e349c36 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ copyright = "2019-{}, {}".format(datetime.now().year, author) # Import project to get version info -sys.path.insert(0, os.path.abspath("../")) +sys.path.insert(0, os.path.abspath(os.path.pardir)) import phys2bids # noqa # The short X.Y version From 8b3a336b0d5941ef63df29d9c2efc45c841a2bac Mon Sep 17 00:00:00 2001 From: Marie-Eve Picard Date: Mon, 8 Jul 2024 15:43:18 -0400 Subject: [PATCH 08/17] Try to fix doc building issue --- .readthedocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 86e746457..fdc5d8ffa 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -20,3 +20,4 @@ python: path: . extra_requirements: - doc + - requirements: requirements.txt From 4c77eca9e5a00c8ec4402b4342bbb51bd49f9060 Mon Sep 17 00:00:00 2001 From: Marie-Eve Picard Date: Mon, 8 Jul 2024 15:47:30 -0400 Subject: [PATCH 09/17] Revert "Try to fix doc building issue" This reverts commit 8b3a336b0d5941ef63df29d9c2efc45c841a2bac. --- .readthedocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index fdc5d8ffa..86e746457 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -20,4 +20,3 @@ python: path: . extra_requirements: - doc - - requirements: requirements.txt From 20aad31ccc02d2a080ee621f57462edab634d3c1 Mon Sep 17 00:00:00 2001 From: Marie-Eve Picard Date: Fri, 25 Oct 2024 11:55:37 -0400 Subject: [PATCH 10/17] fix failing test --- .gitignore | 1 + phys2bids/tests/test_integration.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0d85b61ef..8071c0d29 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,7 @@ instance/ # Sphinx documentation docs/_build/ +docs/_build_doctress/ # PyBuilder target/ diff --git a/phys2bids/tests/test_integration.py b/phys2bids/tests/test_integration.py index 6dad78266..04dd3121b 100644 --- a/phys2bids/tests/test_integration.py +++ b/phys2bids/tests/test_integration.py @@ -128,7 +128,7 @@ def test_integration_heuristic(skip_integration, multifreq_lab_file): assert isfile(join(conversion_path, "call.sh")) # Read logger file - logger_file = glob.glob(join(conversion_path, "*phys2bids*"))[0] + logger_file = glob.glob(join(conversion_path, "*phys2bids*.tsv"))[0] with open(logger_file) as logger_info: logger_info = logger_info.readlines() From 0348a3b3ac9abb5d9b919bd35033a6501a56d676 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:56:07 +0000 Subject: [PATCH 11/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- phys2bids/reporting/html_report.py | 1 + 1 file changed, 1 insertion(+) diff --git a/phys2bids/reporting/html_report.py b/phys2bids/reporting/html_report.py index 49dd4f251..54bcc93a2 100644 --- a/phys2bids/reporting/html_report.py +++ b/phys2bids/reporting/html_report.py @@ -1,4 +1,5 @@ """Reporting functionality for phys2bids.""" + import sys from distutils.dir_util import copy_tree from os.path import join From 2e311e1d0c199c293053359f69d5fc82f030afd9 Mon Sep 17 00:00:00 2001 From: Marie-Eve Picard Date: Fri, 25 Oct 2024 12:11:27 -0400 Subject: [PATCH 12/17] restore .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8071c0d29..0d85b61ef 100644 --- a/.gitignore +++ b/.gitignore @@ -74,7 +74,6 @@ instance/ # Sphinx documentation docs/_build/ -docs/_build_doctress/ # PyBuilder target/ From fe35cc0e42b9257a3e607af5f712ee52368b1132 Mon Sep 17 00:00:00 2001 From: Marie-Eve Picard Date: Fri, 25 Oct 2024 15:50:27 -0400 Subject: [PATCH 13/17] Try fixing RTD error --- .readthedocs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 86e746457..0dcb8d871 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -8,7 +8,7 @@ version: 2 build: os: "ubuntu-20.04" tools: - python: "3.7" + python: "3.9" # Build documentation in the docs/ directory with Sphinx sphinx: @@ -20,3 +20,4 @@ python: path: . extra_requirements: - doc + - reports From 5dad5b53301c4b12f3016140ef5513599dc1484c Mon Sep 17 00:00:00 2001 From: Marie-Eve Picard Date: Fri, 25 Oct 2024 16:02:37 -0400 Subject: [PATCH 14/17] Revert "Try fixing RTD error" This reverts commit fe35cc0e42b9257a3e607af5f712ee52368b1132. --- .readthedocs.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 0dcb8d871..86e746457 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -8,7 +8,7 @@ version: 2 build: os: "ubuntu-20.04" tools: - python: "3.9" + python: "3.7" # Build documentation in the docs/ directory with Sphinx sphinx: @@ -20,4 +20,3 @@ python: path: . extra_requirements: - doc - - reports From 0cc1ae7ff7c4f7f9da304dfd0f3437891532b15c Mon Sep 17 00:00:00 2001 From: Marie-Eve Picard Date: Fri, 25 Oct 2024 16:04:14 -0400 Subject: [PATCH 15/17] Fix RTD error Co-authored-by: Basile Pinsard <1155388+bpinsard@users.noreply.github.com> --- .readthedocs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 86e746457..0dcb8d871 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -8,7 +8,7 @@ version: 2 build: os: "ubuntu-20.04" tools: - python: "3.7" + python: "3.9" # Build documentation in the docs/ directory with Sphinx sphinx: @@ -20,3 +20,4 @@ python: path: . extra_requirements: - doc + - reports From 54076ced6052c1b878ef7a858a9cbae224b6d314 Mon Sep 17 00:00:00 2001 From: Marie-Eve Picard Date: Mon, 2 Dec 2024 16:08:15 -0500 Subject: [PATCH 16/17] Fix output directory tree in report --- phys2bids/phys2bids.py | 3 +-- phys2bids/reporting/html_report.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/phys2bids/phys2bids.py b/phys2bids/phys2bids.py index 9999ca663..453b8bcad 100644 --- a/phys2bids/phys2bids.py +++ b/phys2bids/phys2bids.py @@ -542,8 +542,7 @@ def phys2bids( # Only generate report if specified by the user if make_report: - generate_report(conversion_path, logname, phys_out[key]) - + generate_report(outdir, conversion_path, logname, phys_out[key]) def _main(argv=None): options = _get_parser().parse_args(argv) diff --git a/phys2bids/reporting/html_report.py b/phys2bids/reporting/html_report.py index 54bcc93a2..1b44980bb 100644 --- a/phys2bids/reporting/html_report.py +++ b/phys2bids/reporting/html_report.py @@ -193,7 +193,7 @@ def _generate_bokeh_plots(phys_in, figsize=(250, 500)): return script, div -def generate_report(out_dir, log_path, phys_in): +def generate_report(out_dir, conversion_path, log_path, phys_in): """ Plot all the channels for visualizations as linked line plots for dynamic report. @@ -217,15 +217,15 @@ def generate_report(out_dir, log_path, phys_in): # Copy assets into output folder pkgdir = sys.modules["phys2bids"].__path__[0] assets_path = join(pkgdir, "reporting", "assets") - copy_tree(assets_path, join(out_dir, "assets")) + copy_tree(assets_path, join(conversion_path, "assets")) # Read log with open(log_path, "r") as f: log_content = f.read() log_content = log_content.replace("\n", "
") - log_html_path = join(out_dir, "phys2bids_report_log.html") - qc_html_path = join(out_dir, "phys2bids_report.html") + log_html_path = join(conversion_path, "phys2bids_report_log.html") + qc_html_path = join(conversion_path, "phys2bids_report.html") html = _save_as_html(log_html_path, log_content, qc_html_path) From 924ea5fc11f8f52bcd58291ba119c7ced62733f5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:08:41 +0000 Subject: [PATCH 17/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- phys2bids/phys2bids.py | 1 + 1 file changed, 1 insertion(+) diff --git a/phys2bids/phys2bids.py b/phys2bids/phys2bids.py index 453b8bcad..74c1040f0 100644 --- a/phys2bids/phys2bids.py +++ b/phys2bids/phys2bids.py @@ -544,6 +544,7 @@ def phys2bids( if make_report: generate_report(outdir, conversion_path, logname, phys_out[key]) + def _main(argv=None): options = _get_parser().parse_args(argv) phys2bids(**vars(options))