import rnftools
import os
import textwrap
import bs4
from . import DEFAULT_ALLOWED_DELTA
from . import _default_gp_style_func
##############
##############
### REPORT ###
##############
##############
[docs]class Report:
"""Class for an entire report.
Args:
name (str): Name of the report (name of output file and dir).
title (str): Title of the report (if None, then name is used).
description (str): Description of the report.
bam_dirs (list of str): Directories with BAM files (this option is mutually exclusive with the 'panels' option).
panels (list of dicts): Advanced configuration for panels (list of dictionaries with the following keys: 'bam_dir' (mandatory); 'panel_dir', 'name', 'title' (optional)).
allowed_delta (int): Tolerance of difference of coordinates between true (i.e., expected) alignment and real alignment (very important parameter!).
default_x_run ((float,float)): Range for x-axis in GnuPlot plots.
default_y_run ((float,float)): Range for y-axis in GnuPlot plots.
default_pdf_size_cm ((float,float)): Legacy parameter (does not have any effect).
default_svg_size_px ((int,int)): Size of SVG picture.
render_pdf_method (str): Method for svg42pdf to render PDF (None / 'any' (default) / 'cairo' / 'reportlab' / 'inkscape' / 'imagemagick' / 'wkhtmltopdf').
keep_intermediate_files (bool): Keep files created in intermediate steps during evaluation.
compress_intermediate_files (bool): Compress files created in intermediate steps during evaluation.
default_x_axis (str): Values on x-axis, e.g., "({m}+{w})/({M}+{m}+{w})".
default_x_label (str): Label on x-axis.
gp_style_func (function(i, nb)): Function assigning GnuPlot styles for overall graphs. Arguments: i: 0-based id of curve, nb: number of curves.
"""
# todo: describe format of panels
def __init__(
self,
name,
title=None,
description="",
allowed_delta=DEFAULT_ALLOWED_DELTA,
bam_dirs=None,
panels=None,
default_x_run=(0.00001, 1.0),
default_y_run=(60, 100),
default_pdf_size_cm=(10, 10),
default_svg_size_px=(640, 640),
render_pdf_method='any',
keep_intermediate_files=False,
compress_intermediate_files=True,
default_x_axis="({m}+{w})/({M}+{m}+{w})",
default_x_label="FDR in mapping {{/:Italic(#wrongly mapped reads / #mapped reads)}} ",
gp_style_func=_default_gp_style_func,
):
self._gp_style_func = gp_style_func
rnftools.lavender.add_report(self)
self.name = name
self.report_dir = self.name
self.title = self.name if title == None else title
self.description = description
self.default_x_run = self._load_x_run(default_x_run)
self.default_y_run = self._load_y_run(default_y_run)
self.default_svg_size_px = self._load_svg_size_px(default_svg_size_px)
self.default_x_label = default_x_label
self.render_pdf_method = render_pdf_method
self.allowed_delta = int(allowed_delta)
assert 0 <= allowed_delta
self._html_fn = name + ".html"
assert bam_dirs is None or panels is None, "Panels can be specified using bam_dirs or panels, but not both at the same time."
self.panels = []
if bam_dirs is not None:
assert hasattr(bam_dirs, '__iter__'), "bamdirs should be iterable (list, tuple, etc.)"
# assert isinstance(bam_dirs,collections.iterable)
# assert isinstance(bamdirs, basestring)
self.panels = [
rnftools.lavender.Panel(
bam_dir=bam_dirs[i],
panel_dir=os.path.join(self.report_dir, str(i)),
report=self,
name=str(i),
title="dir {}".format(i),
keep_intermediate_files=keep_intermediate_files,
compress_intermediate_files=compress_intermediate_files,
default_x_axis=default_x_axis,
default_x_label=default_x_label,
gp_style_func=self._gp_style_func,
render_pdf_method=self.render_pdf_method,
) for i in range(len(bam_dirs))
]
if panels is not None:
for i, panel_dict in enumerate(panels):
bam_dir = panel_dict["bam_dir"]
try:
panel_dir = panel_dict["panel_dir"]
except KeyError:
panel_dir = os.path.join(self.report_dir, str(i))
try:
panel_name = panel_dict["name"]
except KeyError:
panel_name = "panel_{}".format(i)
try:
panel_title = panel_dict["title"]
except KeyError:
panel_title = "dir {}".format(i)
self.panels.append(
rnftools.lavender.Panel(
bam_dir=bam_dir,
panel_dir=panel_dir,
report=self,
name=panel_name,
title=panel_title,
keep_intermediate_files=keep_intermediate_files,
compress_intermediate_files=compress_intermediate_files,
default_x_axis=default_x_axis,
default_x_label=default_x_label,
gp_style_func=self._gp_style_func,
render_pdf_method=self.render_pdf_method,
)
)
rnftools.lavender.add_input(self._html_fn)
# first graph
self.add_graph(
"({M}+{m}+{w}+{P}+{x})/{all}", title="Mapped reads in all reads", y_label="#mapped reads / #reads"
)
self.add_graph(
"{M}/({M}+{m}+{w}+{P}+{x})",
title="Correctly mapped reads in all mapped reads",
y_label="#correctly mapped reads / #mapped reads",
)
self.add_graph(
"{M}/({M}+{w}+{x}+{u}+{t}+{P})",
title="Correctly mapped reads in all reads which should be mapped",
y_label="#correctly mapped reads / #reads which should be mapped",
)
self.add_graph(
"({U}+{T})/({u}+{U}+{t}+{T})",
title="Correctly unmapped reads in all unmapped reads",
y_label="#correctly unmapped reads / #unmapped reads",
)
self.add_graph(
"({U}+{T})/({U}+{T}+{m})",
title="Correctly unmapped reads in all reads which should be unmapped",
y_label="#correctly unmapped reads / #reads which should be unmapped",
)
[docs] def add_graph(
self,
y,
x_label=None,
y_label="",
title="",
x_run=None,
y_run=None,
svg_size_px=None,
key_position="bottom right",
):
"""
Add a new graph to the overlap report.
Args:
y (str): Value plotted on y-axis.
x_label (str): Label on x-axis.
y_label (str): Label on y-axis.
title (str): Title of the plot.
x_run ((float,float)): x-range.
y_run ((int,int)): y-rang.
svg_size_px ((int,int): Size of SVG image in pixels.
key_position (str): GnuPlot position of the legend.
"""
if x_run is None:
x_run = self.default_x_run
if y_run is None:
y_run = self.default_y_run
if svg_size_px is None:
svg_size_px = self.default_svg_size_px
for panel in self.panels:
x_run = self._load_x_run(x_run)
y_run = self._load_y_run(y_run)
svg_size_px = self._load_svg_size_px(svg_size_px)
panel.add_graph(
y=y,
x_run=x_run,
y_run=y_run,
svg_size_px=svg_size_px,
y_label=y_label,
x_label=x_label if x_label is not None else self.default_x_label,
title=title,
key_position=key_position,
)
[docs] def get_report_dir(self):
"""Get directory report's auxiliary files."""
return self.report_dir
[docs] def clean(self):
"""Remove all temporary files."""
rnftools.utils.shell('rm -fR "{}" "{}"'.format(self.report_dir, self._html_fn))
[docs] def get_panels(self):
"""Get all contained panels."""
return self.panels
######################################
######################################
[docs] def html_fn(self):
"""Get name of the HTML file of the report."""
return self._html_fn
######################################
######################################
[docs] def create_html(self):
"""Create HTML report."""
html_table = ""
columns = [panel.get_html_column() for panel in self.panels]
trs = len(columns[0])
html_table += os.linesep.join(
[
"<tr>{}</tr>".format("".join(["<td>{}</td>".format(columns[col][row]) for col in range(len(columns))]))
for row in range(trs)
]
)
with open(self._html_fn, "w+") as f:
css_src = textwrap.dedent(
"""\
.main_table {border-collapse:collapse;margin-top:15px;}
td {border: solid #aaaaff 1px;padding:4px;vertical-alignment:top;}
colgroup, thead {border: solid black 2px;padding 2px;}
.configuration {font-size:85%;}
.configuration, .configuration * {margin:0;padding:0;}
.formats {text-align:center;margin:20px 0px;}
img {min-width:640px}
"""
)
html_src = """<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>{title}</title>
<style type="text/css">
{css}
</style>
</head>
<body>
<h1>{title}</h1>
<strong>{description}</strong>
<table class="main_table">
{html_table}
</table>
</body>
""".format(
html_table=html_table,
css=css_src,
title=self.title,
description=self.description,
)
tidy_html_src = bs4.BeautifulSoup(html_src).prettify()
f.write(tidy_html_src)
######################################
######################################
@staticmethod
def _load_x_run(x_run):
assert len(x_run) == 2
to_return = [float(x) for x in x_run]
assert 0.0 < to_return[0] <= 1.0
return to_return
@staticmethod
def _load_y_run(y_run):
assert len(y_run) == 2
to_return = [float(x) for x in y_run]
assert 0.0 <= to_return[0] <= 100.0
assert 0.0 <= to_return[1] <= 100.0
return to_return
@staticmethod
def _load_svg_size_px(svg_size_px):
assert len(svg_size_px) == 2
to_return = [int(x) for x in svg_size_px]
assert 0 <= to_return[0]
assert 0 <= to_return[1]
return to_return