Commit bc6d6b7a authored by bow's avatar bow
Browse files

Add create_report rule

parent a2104e00
......@@ -48,6 +48,7 @@ OUTPUTS = dict(
# Merged FASTQs, stats, and packaged results
fqs="{sample}/{sample}-{pair}.fq.gz",
summary="{sample}/{sample}.summary.json",
report="{sample}/hamlet_report.{sample}.pdf",
package="{sample}/hamlet_results.{sample}.zip",
# Small variants
......@@ -140,6 +141,24 @@ rule create_summary:
" > {output.js}"
rule create_report:
"""Creates a PDF report of the essential results."""
input:
summary=RUN.output(OUTPUTS["summary"]),
css=srcdir("report/assets/style.css"),
templates=srcdir("report/templates"),
imgs=srcdir("report/assets/imgs"),
toc=srcdir("report/assets/toc.xsl"),
scr=srcdir("scripts/generate_report.py"),
output:
pdf=RUN.output(OUTPUTS["report"]),
shell:
"python {input.scr}"
" --templates-dir {input.templates} --imgs-dir {input.imgs}"
" --css-path {input.css} --toc-path {input.toc}"
" {input.summary} {output.pdf}"
rule package_results:
"""Copies essential result files into one directory and zips it."""
input:
......@@ -158,6 +177,7 @@ rule package_results:
kmt_csv=RUN.output(OUTPUTS["kmt2a_csv"]),
kmt_bg_csv=RUN.output(OUTPUTS["kmt2a_bg_csv"]),
kmt_png=RUN.output(OUTPUTS["kmt2a_png"]),
report=RUN.output(OUTPUTS["report"]),
output:
pkg=RUN.output(OUTPUTS["package"]),
params:
......
@import url('https://fonts.googleapis.com/css?family=Lato');
@page {
size: 210mm 297mm;
margin: 30mm 45mm 30mm 45mm;
}
body {
font-family: "Lato", arial, sans-serif;
}
div.cover {
text-align: center;
}
div.cover h1 {
font-size: 3.8em;
font-variant: small-caps;
color: #003d7e;
}
div.cover h2 {
font-size: 3em;
font-variant: small-caps;
margin-top: 15%;
}
div.cover h3 { font-size: 2.34em; }
div.cover h4 { font-size: 2.24em; }
div.cover h5 { font-size: 1.66em; }
div.cover h6 { font-size: 1.50em; }
div.cover p { font-size: 1.50em; }
div.cover .pipeline-subtitle,
div.cover .report-subtitle {
margin-top: -5%;
}
div.cover .pipeline-subtitle,
div.cover .pipeline-trademark {
color: #444;
}
div.cover div.logo {
display: block;
margin-left: auto;
margin-right: auto;
width: 50%;
}
div.cover div.lumc {
margin-top: 20%;
margin-bottom: -2.5%;
}
div.chapter {
page-break-after: always;
margin-bottom: 5em;
}
div.chapter-last {
page-break-after: avoid;
}
div.chapter h1, div.chapter h2, div.chapter h3 {
color: #003d7e;
}
div.chapter a {
color: #007dc2;
text-decoration: none;
}
.keep-together {
page-break-inside: avoid;
}
.break-before {
page-break-before: always;
}
.break-after {
page-break-after: always;
}
/*** Table Styles **/
div.table-title {
display: block;
max-width: 600px;
}
.table-title p {
color: #151515;
font-size: 19px;
font-weight: 400;
font-style: normal;
margin-top: .4em;
margin-bottom: .2em;
}
.table-wide {
background: white;
border-radius: 3px;
border-collapse: collapse;
padding: 1px;
width: 100%;
}
.table-wide th {
color: #fff;
background: #003d7e;
font-weight: normal;
font-size: 15px;
font-variant: small-caps;
padding: .55em;
text-align: left;
vertical-align: middle;
}
.table-wide tr {
color: #444;
font-weight: normal;
}
.table-wide td {
background: #fff;
padding: .5em;
text-align: left;
font-weight: 300;
font-size: 15px;
vertical-align: middle;
}
.table-wide th:first-child {
border-left: 0px;
border-right: 0px;
}
.table-wide tr:first-child th:first-child {
border-top-left-radius: 3px;
}
.table-wide th:last-child {
border-left: 0px;
border-right: 0px;
}
.table-wide tr:first-child th:last-child {
border-top-right-radius: 3px;
}
.table-wide tr:first-child {
border-top: none;
}
.table-wide tr:last-child {
border-bottom: none;
}
.table-wide thead tr:last-child {
border-bottom: 4px solid #809ebf;
}
.table-wide tr:nth-child(odd) td {
background: #e2f0ff;
}
.table-wide tr:last-child td:first-child {
border-bottom-left-radius: 3px;
}
.table-wide tr:last-child td:last-child {
border-bottom-right-radius: 3px;
}
.table-wide td:last-child {
border-right: 0px;
}
.table-wide tbody tr:last-child td {
border-bottom: 1px solid #809ebf;
}
.table-long {
background: white;
border-radius: 3px;
border-collapse: collapse;
padding: 1px;
width: 85%;
table-layout: fixed;
}
.table-long td:nth-child(1) {
background: #003d7e;
color: #fff;
border-right: 3px solid #809ebf;
font-weight: normal;
font-size: 16px;
font-variant: small-caps;
padding: .55em;
text-align: right;
vertical-align: middle;
width: 33.3%;
}
.table-long tr:nth-child(odd) td:not(:first-child) {
background: #e2f0ff;
}
.table-long td:not(:first-child) {
color: #444;
font-weight: 300;
padding: .5em;
vertical-align: middle;
}
.table-long tr:first-child td:first-child {
border-top-left-radius: 3px;
border-left: 0px;
}
.table-long tr:last-child td:first-child {
border-top-right-radius: 3px;
}
.table-long tr:first-child td:not(:first-child) {
border-top: 1px solid #809ebf;
}
.table-long tr:last-child td:not(:first-child) {
border-bottom: 1px solid #809ebf;
}
.table-long tr:last-child td:first-child {
border-bottom-left-radius: 3px;
}
.table-long tr:last-child td:last-child {
border-bottom-right-radius: 3px;
}
.table-long td:last-child {
border-right: 0px;
}
th.text-left,
td.text-left {
text-align: left;
}
th.text-center,
td.text-center {
text-align: center;
}
th.text-right,
td.text-right {
text-align: right;
}
/*** Figure Styles **/
figure img {
display: block;
margin-left: auto;
margin-right: auto;
border-bottom: 2px solid #809ebf;
max-height: 860px;
max-width: 640px;
height: auto;
width: auto;
}
figure img.half {
max-height: 430px;
max-width: 320px;
}
#fusion-imgs {
text-align: center;
border-bottom: 2px solid #809ebf;
}
.fusion {
display: inline-block;
max-height: 430px;
max-width: 300px;
margin-left: auto;
margin-right: auto;
}
figure figcaption {
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
}
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:outline="http://wkhtmltopdf.org/outline"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
indent="yes" />
<xsl:template match="outline:outline">
<html>
<head>
<title>Table of Contents</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
h1 {
text-align: center;
font-size: 20px;
font-family: arial;
}
div {border-bottom: 1px dashed rgb(200,200,200);}
span {float: right;}
li {list-style: none;}
ul {
font-size: 20px;
font-family: arial;
}
ul ul {font-size: 80%; }
ul {padding-left: 0em;}
ul ul {padding-left: 1em;}
a {text-decoration:none; color: black;}
</style>
</head>
<body>
<h1>Table of Contents</h1>
<ul><xsl:apply-templates select="outline:item/outline:item"/></ul>
</body>
</html>
</xsl:template>
<xsl:template match="outline:item">
<li>
<xsl:if test="@title!=''">
<div>
<a>
<xsl:if test="@link">
<xsl:attribute name="href"><xsl:value-of select="@link"/></xsl:attribute>
</xsl:if>
<xsl:if test="@backLink">
<xsl:attribute name="name"><xsl:value-of select="@backLink"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="@title" />
</a>
<span> <xsl:value-of select="@page" /> </span>
</div>
</xsl:if>
<ul>
<xsl:comment>added to prevent self-closing tags in QtXmlPatterns</xsl:comment>
<xsl:apply-templates select="outline:item"/>
</ul>
</li>
</xsl:template>
</xsl:stylesheet>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="{{ css_fname }}">
<title>Hamlet Report</title>
</head>
<body>
<div class="contents">
<div class="chapter">
{% include "contents_basic.html.j2" %}
</div>
<div class="chapter">
{% include "contents_seq.html.j2" %}
</div>
<div class="chapter">
{% include "contents_aln.html.j2" %}
</div>
<div class="chapter">
{% include "contents_rna.html.j2" %}
</div>
<div class="chapter">
{% include "contents_var.html.j2" %}
</div>
<div class="chapter">
{% include "contents_fusion.html.j2" %}
</div>
<div class="chapter">
{% include "contents_itd.html.j2" %}
</div>
<div class="chapter">
{% include "contents_expr.html.j2" %}
</div>
<div class="chapter chapter-last">
{% include "contents_about.html.j2" %}
</div>
</div>
</body>
</html>
<h1>About</h1>
<p>Hamlet is developed jointly by the Hematology Department and Human Genetics Department of <a href="https://www.lumc.nl/">Leiden University Medical Center</a>. It is written using the <a href="https://snakemake.readthedocs.io/en/v4.0.0/">Snakemake workflow system (v4.0)</a>. The source code can be accessed <a href="https://git.lumc.nl/hem/hamlet">here</a>.</p>
<p>Front <a href="https://cnx.org/resources/0b2a8ffb53060940faf88f67d502d5e2a0454c06/1907_Granular_Leukocytes.jpg">cover image</a> taken and modified from <a href="https://cnx.org/contents/14fb4ad7-39a1-4eee-ab6e-3ef2482e3e22">The Anatomy & Physiology Book</a> published on <a href="https://cnx.org">OpenStax</a>.</p>
<h1>Alignment Statistics</h1>
<div class="table-title">
<p>Table 5 | Alignment count statistics</p>
</div>
<table class="table-wide" style="margin-bottom: 2em;">
<thead>
<tr>
<th>Metric</th>
<th class="text-right">Count</th>
<th class="text-right">% of total</th>
<th class="text-right">% of aligned</th>
</tr>
</thead>
<tbody>
<tr>
<td>Total reads</td>
<td class="text-right">{{ stats.aln.num_total_reads|show_int }}</td>
<td class="text-right">100%</td>
<td class="text-right">{{ stats.aln.num_total_reads|show_pct(stats.aln.num_aligned_reads) }}</td>
</tr>
<tr>
<td>Reads aligned</td>
<td class="text-right">{{ stats.aln.num_aligned_reads|show_int }}</td>
<td class="text-right">{{ stats.aln.num_aligned_reads|show_pct(stats.aln.num_total_reads) }}</td>
<td class="text-right">100%</td>
</tr>
<tr style="border-bottom: 1px solid #809ebf;">
<td>Reads aligned properly</td>
<td class="text-right">{{ stats.aln.num_aligned_reads_proper_pairs|show_int }}</td>
<td class="text-right">{{ stats.aln.num_aligned_reads_proper_pairs|show_pct(stats.aln.num_total_reads) }}</td>
<td class="text-right">{{ stats.aln.num_aligned_reads_proper_pairs|show_pct(stats.aln.num_aligned_reads) }}</td>
</tr>
<tr>
<td>Total bases</td>
<td class="text-right">{{ stats.aln.num_total_bases|show_int }}</td>
<td class="text-right">100%</td>
<td class="text-right">{{ stats.aln.num_total_bases|show_pct(stats.aln.num_aligned_bases) }}</td>
</tr>
<tr>
<td>Bases aligned</td>
<td class="text-right">{{ stats.aln.num_aligned_bases|show_int }}</td>
<td class="text-right">{{ stats.aln.num_aligned_bases|show_pct(stats.aln.num_total_bases) }}</td>
<td class="text-right">100%</td>
</tr>
</tbody>
</table>
<div class="table-title">
<p>Table 6 | Insert size statistics</p>
</div>
<table class="table-long" style="margin-bottom: 2em;">
<tbody>
<tr>
<td>Median</td>
<td>{{ stats.ins.median_insert_size|show_int }}</td>
</tr>
<tr>
<td>Median absolute deviation</td>
<td>{{ stats.ins.median_absolute_deviation|show_int }}</td>
</tr>
<tr>
<td>Maximum</td>
<td>{{ stats.ins.max_insert_size|show_int }}</td>
</tr>
<tr>
<td>Minimum</td>
<td>{{ stats.ins.min_insert_size|show_int }}</td>
</tr>
</tbody>
</table>
<div class="table-title">
<p>Table 7 | Other alignment statistics</p>
</div>
<table class="table-long">
<tbody>
<tr>
<td>Strand balance</td>
<td>{{ stats.aln.strand_balance|show_float('.4g') }}</td>
</tr>
<tr>
<td>Mismatch rate</td>
<td>{{ stats.aln.rate_mismatch|show_float('.2e') }}</td>
</tr>
<tr>
<td>Indel rate</td>
<td>{{ stats.aln.rate_indel|show_float('.2e') }}</td>
</tr>
</tbody>
</table>
<h1>Basic Information</h1>
<div class="table-title">
<p>Table 1 | Pipeline and sample details</p>
</div>
<table class="table-long" style="margin-bottom: 2em;">
<tbody>
<tr>
<td>Sample name</td>
<td>{{ metadata.sample_name }}</td>
</tr>
<tr>
<td>Read group count</td>
<td>{{ stats.seq.per_read_group|length }}</td>
</tr>
<tr>
<td>Run name</td>
<td>{{ metadata.run_name }}</td>
</tr>
<tr>
<td>Pipeline version</td>
<td>{{ metadata.pipeline_version }}</td>
</tr>
</tbody>
</table>
<div class="table-title">
<p>Table 2 | Genes of interest defined for analysis</p>
</div>
<table class="table-wide">
<thead>
<tr>
<th class="text-right">No.</th>
<th>Gene symbol</th>
<th>Gene ID</th>
<th>Transcript IDs of interest</th>
</tr>
</thead>
{% for item in metadata.genes_of_interest %}
<tr>
<td class="text-right">{{ loop.index }}.</td>
<td>{{ item.gene_symbol }}</td>
<td>{{ item.gene_id }}</td>
<td>{{ item.transcript_ids|join(', ') }}</td>
</tr>
{% endfor %}
<tbody>
</tbody>
</table>
<h1>Overexpression Analysis Results</h1>
{% set table_idx = metadata.genes_of_interest|num_tids + 11 %}
<div class="table-title">
<p>Table {{ table_idx }} | Selected exon ratio values</p>
</div>
<table class="table-wide">
<thead>
<tr>
<th class="text-left">Exon</th>
<th class="text-right">Base count</th>
<th class="text-left">Divisor gene</th>
<th class="text-right">Divisor base count</th>
<th class="text-right">Ratio</th>
<th class="text-left">Above threshold</th>
</tr>
</thead>
<tbody>
{% for item in results.expr %}
<tr>
<td class="text-left">{{ item.exon }}</td>
<td class="text-right">{{ item.count|show_int }}</td>
<td class="text-left">{{ item.divisor_gene }}</td>
<td class="text-right">{{ item.divisor_exp|show_int }}</td>
<td class="text-right">{{ item.ratio|show_float }}</td>
<td class="text-left">{{ item.above_threshold }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<h1>Fusion Detection Results</h1>
{% set figure_idx = metadata.genes_of_interest|length + 1 %}
{% set table_idx = metadata.genes_of_interest|num_tids + 10 %}
<div class="keep-together">
{% if results.fusion.intersected %}
<figure>