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

feat: Rewritten reports #1470

Merged
merged 47 commits into from Mar 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
c3787e4
started rewriting report template
johanneskoester Jan 28, 2022
683b052
fixes
johanneskoester Feb 2, 2022
fa6b5dd
minor
johanneskoester Feb 7, 2022
68ef13b
Merge branch 'main' into report-2.0
johanneskoester Feb 18, 2022
ea3dd56
polishing
johanneskoester Feb 18, 2022
53ab61a
Merge branch 'main' into report-2.0
johanneskoester Mar 6, 2022
22c1eb1
react+tailwind based report (experiment)
johanneskoester Mar 9, 2022
82ebfde
refactor
johanneskoester Mar 9, 2022
e257b9f
fixes and refactoring
johanneskoester Mar 9, 2022
1ee686f
image display
johanneskoester Mar 9, 2022
3c81ff5
html handling
johanneskoester Mar 9, 2022
f20ccd3
fixes
johanneskoester Mar 9, 2022
64a9855
show/hide and papercut fixes
johanneskoester Mar 9, 2022
6c0a2d1
move new report into module
johanneskoester Mar 9, 2022
4ee49b1
fixes
johanneskoester Mar 10, 2022
f22d6cc
cleanup
johanneskoester Mar 10, 2022
ea21e3e
fixes
johanneskoester Mar 10, 2022
2db9dcf
fix
johanneskoester Mar 10, 2022
ffae548
fixes
johanneskoester Mar 10, 2022
1fb4399
fixes
johanneskoester Mar 10, 2022
20a8dc2
dbg
johanneskoester Mar 10, 2022
8930221
dbg
johanneskoester Mar 10, 2022
c90ec18
cleanup
johanneskoester Mar 10, 2022
ee403a8
implement column support
johanneskoester Mar 10, 2022
60030c8
fixes
johanneskoester Mar 10, 2022
cdb8af6
polish
johanneskoester Mar 10, 2022
4928be9
allow input functions for report category, subcategory, and columns
johanneskoester Mar 10, 2022
079cf8d
rename columns to labels
johanneskoester Mar 10, 2022
77f97c5
use chevron icon for breadcrumbs
johanneskoester Mar 10, 2022
824e0cb
fix
johanneskoester Mar 10, 2022
7297c37
padding
johanneskoester Mar 10, 2022
7278577
overflow and code display polish
johanneskoester Mar 10, 2022
8be2830
scrolling fixes
johanneskoester Mar 10, 2022
4beb587
static tailwind
johanneskoester Mar 10, 2022
b0df797
loading screen
johanneskoester Mar 10, 2022
e3246a6
fixes and cleanup
johanneskoester Mar 10, 2022
eeffe48
fmt
johanneskoester Mar 11, 2022
1249dba
fix zip mode
johanneskoester Mar 11, 2022
9c7129a
pdf display
johanneskoester Mar 11, 2022
00fb706
add example pdf
johanneskoester Mar 11, 2022
f758e14
fixes
johanneskoester Mar 11, 2022
bbe6fb9
cleanup
johanneskoester Mar 11, 2022
f966475
add graphviz for testing
johanneskoester Mar 11, 2022
fc6faf5
fix lints and add hover and tooltip effects to rulegraph
johanneskoester Mar 11, 2022
663a8b8
overflow fixes and link support (draft)
johanneskoester Mar 11, 2022
a96af49
fixes, links, brand
johanneskoester Mar 12, 2022
2bc167b
cleanup
johanneskoester Mar 12, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 45 additions & 2 deletions docs/snakefiles/reporting.rst
Expand Up @@ -52,7 +52,11 @@ Consider the following example:

rule d:
output:
report(directory("testdir"), patterns=["{name}.txt"], caption="report/somedata.rst", category="Step 3")
report(
directory("testdir"),
patterns=["{name}.txt"],
caption="report/somedata.rst",
category="Step 3")
shell:
"mkdir {output}; for i in 1 2 3; do echo $i > {output}/$i.txt; done"

Expand Down Expand Up @@ -89,8 +93,44 @@ This works as follows:
echo \"alert('test')\" > test/js/test.js
"""

Defining file labels
~~~~~~~~~~~~~~~~~~~~~

Moreover, in every ``.rst`` document, you can link to
In addition to category, and subcategory, it is possible to define a dictionary of labels for each report item.
By that, the actual filename will be hidden in the report and instead a table with the label keys as columns and the values in the respective row for the file will be displayed.
This can lead to less technical reports that abstract away the fact that the results of the analysis are actually files.
Consider the following modification of rule ``b`` from above:

.. code-block:: python

rule b:
input:
expand("{model}.{i}.out", i=range(10))
output:
report(
"fig2.png",
caption="report/fig2.rst",
category="Step 2",
subcategory="{model}",
labels={
"model": "{model}",
"figure": "some plot"
}
)
shell:
"sleep `shuf -i 1-3 -n 1`; cp data/fig2.png {output}"


Determining category, subcategory, and labels dynamically via functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Similar to e.g. with input file and parameter definition (see :ref:`snakefiles-input_functions`), ``category`` and a ``subcategory`` and ``labels`` can be specified by pointing to a function which is expected to return a string.


Linking between items
~~~~~~~~~~~~~~~~~~~~~

In every ``.rst`` document, you can link to

* the **Workflow** panel (with ``Rules_``),
* the **Statistics** panel (with ``Statistics_``),
Expand All @@ -99,6 +139,9 @@ Moreover, in every ``.rst`` document, you can link to

For details about the hyperlink mechanism of restructured text see `here <https://docutils.sourceforge.io/docs/user/rst/quickref.html#hyperlink-targets>`_.

Rendering reports
~~~~~~~~~~~~~~~~~

To create the report simply run

.. code-block:: bash
Expand Down
6 changes: 4 additions & 2 deletions setup.py
Expand Up @@ -41,6 +41,8 @@
"snakemake",
"snakemake.remote",
"snakemake.report",
"snakemake.report.template",
"snakemake.report.data",
"snakemake.common",
"snakemake.caching",
"snakemake.deployment",
Expand All @@ -56,7 +58,7 @@
"snakemake-bash-completion = snakemake:bash_completion",
]
},
package_data={"": ["*.css", "*.sh", "*.html", "*.jinja2"]},
package_data={"": ["*.css", "*.sh", "*.html", "*.jinja2", "*.js", "*.svg"]},
install_requires=[
"wrapt",
"requests",
Expand All @@ -82,7 +84,7 @@
"retry",
],
extras_require={
"reports": ["jinja2", "networkx", "pygments", "pygraphviz"],
"reports": ["jinja2", "pygments"],
"messaging": ["slacker"],
"google-cloud": [
"oauth2client",
Expand Down
10 changes: 10 additions & 0 deletions snakemake/common/__init__.py
Expand Up @@ -257,3 +257,13 @@ class Gather:
"""A namespace for gather to allow items to be accessed via dot notation."""

pass


def get_function_params(func):
return inspect.signature(func).parameters


def get_input_function_aux_params(func, candidate_params):
func_params = get_function_params(func)

return {k: v for k, v in candidate_params.items() if k in func_params}
22 changes: 22 additions & 0 deletions snakemake/dag.py
Expand Up @@ -2235,6 +2235,28 @@ def stats(self):
yield tabulate(rows, headers="keys")
yield ""

def toposorted(self, jobs=None, inherit_pipe_dependencies=False):
from toposort import toposort

if jobs is None:
jobs = set(self.jobs)

def get_dependencies(job):
for dep, files in self.dependencies[job].items():
if dep in jobs:
yield dep
if inherit_pipe_dependencies and any(
is_flagged(f, "pipe") for f in files
):
# In case of a pipe, inherit the dependencies of the producer,
# such that the two jobs end up on the same toposort level.
# This is important because they are executed simulataneously.
yield from get_dependencies(dep)

dag = {job: set(get_dependencies(job)) for job in jobs}

return toposort(dag)

def get_outputs_with_changes(self, change_type, include_needrun=True):
is_changed = lambda job: (
getattr(self.workflow.persistence, f"{change_type}_changed")(job)
Expand Down
19 changes: 14 additions & 5 deletions snakemake/io.py
Expand Up @@ -1080,12 +1080,19 @@ def checkpoint_target(value):


ReportObject = collections.namedtuple(
"ReportObject", ["caption", "category", "subcategory", "patterns", "htmlindex"]
"ReportObject",
["caption", "category", "subcategory", "labels", "patterns", "htmlindex"],
)


def report(
value, caption=None, category=None, subcategory=None, patterns=[], htmlindex=None
value,
caption=None,
category=None,
subcategory=None,
labels=None,
patterns=[],
htmlindex=None,
):
"""Flag output file or directory as to be included into reports.

Expand All @@ -1094,15 +1101,17 @@ def report(
Arguments
value -- File or directory.
caption -- Path to a .rst file with a textual description of the result.
category -- Name of the category in which the result should be displayed in the report.
pattern -- Wildcard pattern for selecting files if a directory is given (this is used as
category -- Name of the (optional) category in which the result should be displayed in the report.
subcategory -- Name of the (optional) subcategory
columns -- Dict of strings (may contain wildcard expressions) that will be used as columns when displaying result tables
patterns -- Wildcard patterns for selecting files if a directory is given (this is used as
input for snakemake.io.glob_wildcards). Pattern shall not include the path to the
directory itself.
"""
return flag(
value,
"report",
ReportObject(caption, category, subcategory, patterns, htmlindex),
ReportObject(caption, category, subcategory, labels, patterns, htmlindex),
)


Expand Down
17 changes: 3 additions & 14 deletions snakemake/jobs.py
Expand Up @@ -1179,20 +1179,9 @@ def finalize(self):
from toposort import toposort

if self.toposorted is None:

def get_dependencies(job):
for dep, files in self.dag.dependencies[job].items():
if dep in self.jobs:
yield dep
if any(is_flagged(f, "pipe") for f in files):
# In case of a pipe, inherit the dependencies of the producer,
# such that the two jobs end up on the same toposort level.
# This is important because they are executed simulataneously.
yield from get_dependencies(dep)

dag = {job: set(get_dependencies(job)) for job in self.jobs}

self.toposorted = list(toposort(dag))
self.toposorted = list(
self.dag.toposorted(jobs=self.jobs, inherit_pipe_dependencies=True)
)

@property
def all_products(self):
Expand Down