import rnftools.lavender
import rnftools.utils
import snakemake
import os
import glob
import tarfile
import io
import re
import textwrap
from . import _default_gp_style_func
from svg42pdf import svg42pdf
#############
#############
### PANEL ###
#############
#############
[docs]class Panel:
"""Class for a single panel in a HTML report.
Args:
report (rnftools.lavender.Report): The owner report.
bam_dir (str): Directory to the BAM files for this panel.
panel_dir (str): Directory with auxiliary files for this panel.
name (str): Name of the panel (used for CSS, etc.).
title (str): Title of the panel (to be displayed).
render_pdf_method (str): Method for svg42pdf to render PDF (None / 'any' / '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.
Raises:
ValueError
"""
def __init__(
self,
report,
bam_dir,
panel_dir,
name,
title,
render_pdf_method,
keep_intermediate_files,
compress_intermediate_files,
default_x_axis,
default_x_label,
gp_style_func=_default_gp_style_func,
):
self.report = report
rnftools.lavender.add_panel(self)
self.name = name
self.title = title
self.panel_dir = panel_dir
self.default_x_axis = default_x_axis
self.default_x_label = default_x_label
self.render_pdf_method = render_pdf_method
self.gp_plots = []
self._gp_style_func = gp_style_func
# simple test
nb_styles = 10
for i in range(nb_styles):
res = self._gp_style_func(i, nb_styles)
assert isinstance(res, str)
assert len(res) > 0
self._gp_fn = os.path.join(self.panel_dir, "gp", "_combined.gp")
self._tar_fn = os.path.join(
self.panel_dir, "tar", "{title}.{panel}.tar".format(title=self.report.title, panel=self.title)
)
self._svg_fns = []
self._pdf_fns = []
bams_fns = glob.glob(os.path.join(bam_dir, "*.bam"))
self.bams = [
rnftools.lavender.Bam(
bam_fn=bam_fn,
panel=self,
name=os.path.basename(bam_fn).replace(".bam", ""),
render_pdf_method=self.render_pdf_method,
keep_intermediate_files=keep_intermediate_files,
compress_intermediate_files=compress_intermediate_files,
default_x_axis=default_x_axis,
default_x_label=default_x_label,
) for bam_fn in sorted(bams_fns)
]
if len(self.bams) == 0:
raise ValueError("Panel '{}' does not contain any BAM file.".format(self.name))
for x in ["gp", "html", "roc", "graphics", "tar"]:
rnftools.utils.shell('mkdir -p "{}"'.format(os.path.join(self.panel_dir, x)))
[docs] def get_report(self):
""" Get the report. """
return self.report
[docs] def get_panel_dir(self):
""" Get the directory with panel's auxiliary files. """
return self.panel_dir
[docs] def get_bams(self):
""" Get BAMs for this panel. """
return self.bams
[docs] def get_required_files(self):
""" Get all required files. """
return [bam.get_required_files() for bam in self.bams] + [self._svg_fns]
[docs] def get_html_column(self):
""" Get a HTML column for this panel. """
panel_id = "panel_{}".format(self.name)
return ["<h2>{}</h2>".format(self.title) + '<a href="{}">Download data</a>'.format(self.tar_fn())] + [
# list of links
(" <br />" + os.linesep).join(
[
"""
<strong>{bam_name}:</strong>
<a onclick="document.getElementById('{panel_id}').src='{bam_svg}';document.getElementById('{panel_id}_').href='{bam_html}';return false;" href="#">display graph</a>,
<a href="{bam_html}">detailed report</a>
""".format(
bam_name=bam.get_name(),
bam_html=bam.html_fn(),
bam_svg=bam.svg_fn(),
panel_id=panel_id,
) for bam in self.bams
]
) + '<br /> '.format(self.tar_fn()),
# main graph
"""
<div class="formats">
<a href="{html}" id="{panel_id}_">
<img src="{svg}" id="{panel_id}" />
</a>
</div>
""".format(
html=self.bams[0]._html_fn,
svg=self.bams[0]._svg_fn,
panel_id=panel_id,
),
] + [
# overall graphs
"""
<div class="formats">
<img src="{svg}" />
<br />
<a href="{svg}">SVG version</a>
|
<a href="{gp}" type="text/plain">GP file</a>
</div>
""".format(
svg=svg,
gp=self._gp_fn,
) for svg in self._svg_fns
]
######################################
######################################
[docs] def tar_fn(self):
""" Get the TAR file name for the archive with data. """
return self._tar_fn
[docs] def gp_fn(self):
""" Get the GnuPlot file name for the overall graphs. """
return self._gp_fn
[docs] def svg_fns(self):
""" Get the PDF file names for the overall graphs (empty list if they are not rendered). """
return self._svg_fns
def pdf_fns(self):
if self.render_pdf_method is None:
return []
else:
return [re.sub(r'\.svg$', r'.pdf', svg_fn) for svg_fn in self._svg_fns]
######################################
######################################
def add_graph(
self,
y,
y_label,
x_label,
title,
x_run,
y_run,
svg_size_px,
key_position,
):
x_gp = rnftools.lavender._format_xxx(self.default_x_axis)
y_gp = rnftools.lavender._format_xxx("({})*100".format(y))
i = len(self.gp_plots)
svg_file = os.path.join(self.panel_dir, "graphics", "_combined_{}.svg".format(i))
self._svg_fns.append(svg_file)
params = [
"##################################################",
'set title "{{/:Bold {}}}"'.format(title),
'set key {}'.format(key_position),
'set x2lab "{}"'.format(x_label),
'set ylab "{}"'.format(y_label),
'set xran [{xran}]'.format(xran="{:.10f}:{:.10f}".format(x_run[0], x_run[1])),
'set x2ran [{xran}]'.format(xran="{:.10f}:{:.10f}".format(x_run[0], x_run[1])),
'set yran [{yran}]'.format(yran="{:.10f}:{:.10f}".format(y_run[0], y_run[1]), ),
'set y2ran [{yran}]'.format(yran="{:.10f}:{:.10f}".format(y_run[0], y_run[1]), ),
"",
]
plot = [
""""{roc_fn}" using ({x}):({y}) with lp ls {i} ps 0.8 title " {basename}" noenhanced,\\""".format(
x=x_gp,
y=y_gp,
roc_fn=self.bams[i].roc_fn(),
basename=os.path.basename(self.bams[i].roc_fn())[:-4],
i=i + 1,
) for i in range(len(self.bams))
]
self.gp_plots.append(
os.linesep.join(
[
"set termin svg size {svg_size} enhanced".
format(svg_size="{},{}".format(svg_size_px[0], svg_size_px[1])),
'set out "{}"'.format(svg_file),
'set key spacing 0.8 opaque',
] + params + ["plot \\"] + plot + ["", ""]
)
)
[docs] def create_gp(self):
""" Create GnuPlot file. """
nb_bams = len(self.bams)
gp_parts = [
textwrap.dedent(
"""\
set log x
set log x2
#set format x "10^{{%L}}"
set format x2 "10^{{%L}}"
set x2tics
unset xtics
"""
),
os.linesep.join([self._gp_style_func(i, nb_bams) for i in range(nb_bams)]),
textwrap.dedent(
"""\
set format y "%g %%"
set ytics
set pointsize 1.5
set grid ytics lc rgb "#777777" lw 1 lt 0 front
set grid x2tics lc rgb "#777777" lw 1 lt 0 front
set datafile separator "\\t"
set palette negative
"""
),
os.linesep.join(self.gp_plots)
]
gp_src = os.linesep.join(gp_parts)
# .format(
# x_lab=self.default_x_label,
# )
with open(self._gp_fn, "w+") as f:
f.write(gp_src)
[docs] def create_graphics(self):
"""Create images related to this panel."""
if len(self._svg_fns) > 0:
rnftools.utils.shell('"{}" "{}"'.format("gnuplot", self._gp_fn))
if self.render_pdf_method is not None:
for svg_fn in self._svg_fns:
pdf_fn = re.sub(r'\.svg$', r'.pdf', svg_fn)
svg42pdf(svg_fn, pdf_fn, method=self.render_pdf_method)
[docs] def create_tar(self):
"""Create a tar file with all the files."""
def add_file_to_tar(tar, orig_fn, new_fn, func=None):
tf = tarfile.TarInfo(name=new_fn)
with open(orig_fn) as f:
tfs = f.read()
if func is not None:
tfs = func(tfs)
tf.size = len(tfs)
tfs = io.BytesIO(tfs.encode('utf8'))
tar.addfile(tarinfo=tf, fileobj=tfs)
def add_text_to_tar(tar, new_fn, text, func=None):
tf = tarfile.TarInfo(name=new_fn)
if func is not None:
text = func(text)
tf.size = len(text)
tfs = io.BytesIO(text.encode('utf8'))
tar.addfile(tarinfo=tf, fileobj=tfs)
def strip_lines(text):
text = text.replace("\t", " ")
while text.find(" ") != -1:
text = text.replace(" ", " ")
lines = [x.strip() for x in text.strip().split("\n")]
return "\n".join(lines) + "\n"
tar = tarfile.TarFile(self._tar_fn, "w")
for i in range(len(self.bams)):
roc_fn = self.bams[i].roc_fn()
t_roc_fn = os.path.basename(roc_fn)
gp_fn = self.bams[i].gp_fn()
t_gp_fn = os.path.basename(gp_fn)
svg_fn = self.bams[i].svg_fn()
t_svg_fn = os.path.basename(svg_fn)
add_file_to_tar(tar, roc_fn, t_roc_fn)
add_file_to_tar(
tar, gp_fn, t_gp_fn, lambda x: strip_lines(x.replace(roc_fn, t_roc_fn).replace(svg_fn, t_svg_fn))
)
gp_fn = self._gp_fn
t_gp_fn = os.path.basename(gp_fn)
svg_dir = os.path.join(self.panel_dir, "graphics") + "/"
roc_dir = os.path.join(self.panel_dir, "roc") + "/"
add_file_to_tar(tar, gp_fn, t_gp_fn, lambda x: strip_lines(x.replace(svg_dir, "").replace(roc_dir, "")))
makefile = [
".PHONY: all",
"all:",
"\tgnuplot *.gp",
"clean:",
"\trm -f *.svg",
"",
]
add_text_to_tar(tar, "Makefile", "\n".join(makefile))
# for svg_fn in self._svg_fns:
# t_roc_fn=os.path.basename(svg_fn)
# add_to_tar(tar,svg_fn,t_svg_fn)