Merge branch 'table_to_aplanat' into 'dev'

Moved fiterable table into aplanat and general tidy up

See merge request epi2melabs/workflow-containers/wf-isoforms!36
This commit is contained in:
Neil Horner 2022-02-28 14:17:46 +00:00
commit b56cec9994
8 changed files with 34 additions and 277 deletions

View File

@ -3,7 +3,6 @@
import argparse import argparse
from collections import Counter, defaultdict, OrderedDict from collections import Counter, defaultdict, OrderedDict
from functools import reduce
import math import math
from pathlib import Path from pathlib import Path
@ -19,7 +18,6 @@ from bokeh.palettes import Category10_10
from bokeh.plotting import figure from bokeh.plotting import figure
from bokeh.transform import dodge from bokeh.transform import dodge
import gffutils import gffutils
from jinja2 import Template
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import sigfig import sigfig
@ -50,30 +48,6 @@ def _hbar(y, right, title='', fig_height=300, fig_width=300,
return fig return fig
class Table:
"""A table report component.
Adapted from aplanat
"""
def __init__(self, template, data_frame, index, table_id, **kwargs):
"""Initialize table component.
:param dataframe: dataframe to turn in to simple table.
"""
template = Template(template)
for key, val in kwargs.items():
if isinstance(val, bool):
kwargs[key] = str(val).lower()
self.div = template.render(dataframe=data_frame.to_html(
table_id=table_id,
index=index),
table_id=table_id,
kwargs=kwargs)
def simple_hbar(df, y, right, title="", color=Colors.cerulean, def simple_hbar(df, y, right, title="", color=Colors.cerulean,
fig_kwargs={}, plot_kwargs={}): fig_kwargs={}, plot_kwargs={}):
"""Create a simple barplot. """Create a simple barplot.
@ -461,33 +435,22 @@ def gff_compare_plots(report, gffcompare_outdirs: Path, sample_ids):
tracking['Overlaps'].values.tolist(), tracking['Overlaps'].values.tolist(),
tracking['Percent'].values.tolist(), title="{}".format(id_)) tracking['Percent'].values.tolist(), title="{}".format(id_))
# Edit for creating unified table tracking.drop(columns=['sample_id'], inplace=True)
tracking.rename(columns={'sample_id': id_ + ' count'}, inplace=True)
tracking.drop(columns=['Count'], inplace=True)
tracking_dfs.append(tracking) tracking_dfs.append(tracking)
# section.plot(grid) tracking['description'] = pd.Series(tracking.Overlaps.apply(
# Tracking table
df_class_table = reduce(
lambda left, right: pd.merge(left, right), tracking_dfs)
desc = pd.Series(df_class_table.Overlaps.apply(
lambda x: x.split(':')[0])) lambda x: x.split(':')[0]))
df_class_table.insert(0, 'description', desc)
code = pd.Series(df_class_table.Overlaps.apply( tracking['code'] = pd.Series(tracking.Overlaps.apply(
lambda x: x.split(':')[1])) lambda x: x.split(':')[1]))
df_class_table.insert(0, 'code', code)
cols = [TableColumn(field=Ci, title=Ci, width=100) cols = [TableColumn(field=Ci, title=Ci, width=100)
for Ci in df_class_table.columns] for Ci in tracking.columns]
track_table = DataTable(columns=cols, track_table = DataTable(columns=cols,
source=ColumnDataSource(df_class_table), source=ColumnDataSource(tracking),
index_position=None, index_position=None,
width=500) width=500)
df_class_table.drop(columns=['Overlaps'], inplace=True)
tabs.append(Panel( tabs.append(Panel(
child=gridplot([track_bar, track_table], ncols=2), title=id_) child=gridplot([track_bar, track_table], ncols=2), title=id_)
) )
@ -654,7 +617,7 @@ def cluster_quality(cluster_qc_dir, report, sample_ids):
section.plot(cover_panel) section.plot(cover_panel)
def transcript_table(report, df_tmaps, covr_threshold, table_template): def transcript_table(report, df_tmaps, covr_threshold):
"""Create searchable table of transcripts.""" """Create searchable table of transcripts."""
section = report.add_section() section = report.add_section()
@ -701,11 +664,7 @@ def transcript_table(report, df_tmaps, covr_threshold, table_template):
df.sort_values('parent gene iso num', inplace=True, ascending=True) df.sort_values('parent gene iso num', inplace=True, ascending=True)
with open(table_template, 'r') as fh: section.table(df, index=False)
tabletempl = fh.read()
bigtable = Table(tabletempl, df, index=False, table_id='bigtable')
section._add_item(bigtable.div)
def tanscriptome_summary(report, gffs, sample_ids, denovo=False): def tanscriptome_summary(report, gffs, sample_ids, denovo=False):
@ -868,12 +827,6 @@ def main():
parser.add_argument( parser.add_argument(
"--sample_ids", required=True, nargs='+', "--sample_ids", required=True, nargs='+',
help="List of sample ids") help="List of sample ids")
parser.add_argument(
"--report_template", required=True,
help="Jinja template")
parser.add_argument(
"--table_template", required=True,
help="Template for big transcript table")
parser.add_argument( parser.add_argument(
"--transcript_table_cov_thresh", required=False, type=int, default=50, "--transcript_table_cov_thresh", required=False, type=int, default=50,
help="Isoforms without this support will be excluded from the table") help="Isoforms without this support will be excluded from the table")
@ -932,14 +885,8 @@ def main():
if len(pc_df) > 0: if len(pc_df) > 0:
pychopper_plots(report, pc_df) pychopper_plots(report, pc_df)
with open(args.report_template, "r") as fh:
reptempl = fh.read()
report.template = Template(reptempl)
if df_tmaps is not None: if df_tmaps is not None:
transcript_table(report, df_tmaps, args.transcript_table_cov_thresh, transcript_table(report, df_tmaps, args.transcript_table_cov_thresh)
args.table_template)
if args.cluster_qc_dirs is not None: if args.cluster_qc_dirs is not None:
cluster_quality(args.cluster_qc_dirs, report, sample_ids) cluster_quality(args.cluster_qc_dirs, report, sample_ids)

View File

@ -1,42 +0,0 @@
<!doctype html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>wf-isoforms report</title>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu"
crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/simple-datatables@latest/dist/style.css"
rel="stylesheet" type="text/css">
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@latest"
type="text/javascript"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script
src="https://code.jquery.com/jquery-3.6.0.slim.min.js"
integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI="
crossorigin="anonymous"></script>
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"
type="text/javascript"></script>
<link rel="stylesheet"
href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css"
type="text/css">
{{ resources }}
{{ script }}
<!-- delete?-->
{{ bigtable_js }}
</head>
<body>
<div class="container">
<h1>{{ title }}</h1>
<p class="lead">{{ lead }}
{{ div }}
<div class="container" id="bigtable">
{{ big_table }}
</div>
</body>
</html>

View File

@ -1,106 +0,0 @@
<script type="text/javascript">
$(document).ready(function(){
$("#load_msg").hide();
$("#{{ table_id }}").show();
});
</script>
<style>
table{
table-layout: fixed;
word-wrap: break-word;
}
#{{ table_id }}{
<!-- To make the column widths smaller -->
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
display: none;
}
#{{ table_id }} td, #{{ table_id }} th {
border: 1px solid #ddd;
padding: 4px;
}
#{{ table_id }} tr:nth-child(even){background-color: #f2f2f2;}
#{{ table_id }} tr:hover {background-color: #90C5E7;}
#{{ table_id }} th {
padding-top: 4px;
padding-bottom: 4px;
text-align: left;
background-color: #0084A9;
}
</style>
<body>
<div id='load_msg'>Table loading</div>
{{ dataframe }}
</body>
<script type="text/javascript">
$(document).ready(function () {
// Setup - add a text input to each footer cell
$('#{{ table_id }} thead tr')
.clone(true)
.addClass('filters')
.appendTo('#{{ table_id }} thead');
var table = $('#{{ table_id }}').DataTable({
"columnDefs": [
{ "width": "5%", "targets": [2, 4] }],
sDom: 'lrtip', // removes search box while still allowing search
searching: true,
pageLength: 30,
orderCellsTop: true,
fixedHeader: true,
initComplete: function () {
var api = this.api();
// For each column
api
.columns()
.eq(0)
.each(function (colIdx) {
// Set the header cell to contain the input element
var cell = $('.filters th').eq(
$(api.column(colIdx).header()).index()
);
var title = $(cell).text();
$(cell).html('<input type="text" placeholder="&#xF002; Search" style="font-family:Arial, FontAwesome; width:100%" />');
// On every keypress in this input
$(
'input',
$('.filters th').eq($(api.column(colIdx).header()).index())
)
.off('keyup change')
.on('keyup change', function (e) {
e.stopPropagation();
// Get the search value
$(this).attr('title', $(this).val());
var regexr = '({search})'; //$(this).parents('th').find('select').val();
var cursorPosition = this.selectionStart;
// Search the column for that value
api
.column(colIdx)
.search(
this.value != ''
? regexr.replace('{search}', '(((' + this.value + ')))')
: '',
this.value != '',
this.value == ''
)
.draw();
$(this)
.focus()[0]
.setSelectionRange(cursorPosition, cursorPosition);
});
});
},
});
});
</script>

1
evaluation/tests.sh Normal file → Executable file
View File

@ -26,7 +26,6 @@ multisampledir="test_data/demultiplexed_fastq"
#"--minimap2_opts '-uf --splice-flank=no'" #"--minimap2_opts '-uf --splice-flank=no'"
results=() results=()
OUTPUT=$1/denovo_multi_sample_no_ref_genome; OUTPUT=$1/denovo_multi_sample_no_ref_genome;
nextflow run . --fastq $multisampledir $config --denovo --ref_genome test_data/SIRV_150601a.fasta -profile local --out_dir ${OUTPUT} -w ${OUTPUT}/workspace \ nextflow run . --fastq $multisampledir $config --denovo --ref_genome test_data/SIRV_150601a.fasta -profile local --out_dir ${OUTPUT} -w ${OUTPUT}/workspace \
--sample_sheet test_data/sample_sheet -resume; --sample_sheet test_data/sample_sheet -resume;

76
main.nf
View File

@ -42,7 +42,6 @@ process getVersions {
python -c "import pysam; print(f'pysam,{pysam.__version__}')" >> versions.txt python -c "import pysam; print(f'pysam,{pysam.__version__}')" >> versions.txt
python -c "import aplanat; print(f'aplanat,{aplanat.__version__}')" >> versions.txt python -c "import aplanat; print(f'aplanat,{aplanat.__version__}')" >> versions.txt
python -c "import pandas; print(f'pandas,{pandas.__version__}')" >> versions.txt python -c "import pandas; print(f'pandas,{pandas.__version__}')" >> versions.txt
python -c "import sklearn; print(f'scikit-learn,{sklearn.__version__}')" >> versions.txt
fastcat --version | sed 's/^/fastcat,/' >> versions.txt fastcat --version | sed 's/^/fastcat,/' >> versions.txt
minimap2 --version | sed 's/^/minimap2,/' >> versions.txt minimap2 --version | sed 's/^/minimap2,/' >> versions.txt
samtools --version | head -n 1 | sed 's/ /,/' >> versions.txt samtools --version | head -n 1 | sed 's/ /,/' >> versions.txt
@ -87,15 +86,9 @@ process preprocess_reads {
tuple val(sample_id), path('*.tsv'), emit: report tuple val(sample_id), path('*.tsv'), emit: report
script: script:
""" """
if [[ ${params.use_pychopper} == true ]]; cdna_classifier.py -t ${params.threads} ${params.pychopper_opts} ${input_reads} ${sample_id}_full_length_reads.fq
then mv cdna_classifier_report.tsv ${sample_id}_cdna_classifier_report.tsv
cdna_classifier.py -t ${params.threads} ${params.pychopper_opts} ${input_reads} ${sample_id}_full_length_reads.fq generate_pychopper_stats.py --data ${sample_id}_cdna_classifier_report.tsv --output .
mv cdna_classifier_report.tsv ${sample_id}_cdna_classifier_report.tsv
generate_pychopper_stats.py --data ${sample_id}_cdna_classifier_report.tsv --output .
else
ln -s `realpath $input_reads` "${sample_id}_full_length_reads.fq"
touch $sample_id}_cdna_classifier_report.tsv
fi
""" """
} }
@ -267,8 +260,6 @@ process makeReport {
label "isoforms" label "isoforms"
input: input:
path report_template
path table_template
path versions path versions
path "params.json" path "params.json"
val denovo val denovo
@ -289,8 +280,6 @@ process makeReport {
def OPT_DENOVO = denovo ? "--denovo" : '' def OPT_DENOVO = denovo ? "--denovo" : ''
""" """
report.py --report $report_name \ report.py --report $report_name \
--report_template $report_template \
--table_template $table_template \
--versions $versions \ --versions $versions \
--params params.json \ --params params.json \
$OPT_ALN \ $OPT_ALN \
@ -301,8 +290,6 @@ process makeReport {
--gff_annotation $gff_annotation \ --gff_annotation $gff_annotation \
--transcript_table_cov_thresh $params.transcript_table_cov_thresh \ --transcript_table_cov_thresh $params.transcript_table_cov_thresh \
$OPT_DENOVO $OPT_DENOVO
""" """
} }
@ -311,43 +298,24 @@ process makeReport {
// decoupling the publish from the process steps. // decoupling the publish from the process steps.
process output { process output {
// publish inputs to output directory // publish inputs to output directory
label "isoforms" publishDir "${params.out_dir}", mode: 'copy', pattern: "*"
publishDir "${params.results_dir}/${sample_id}", mode: 'copy', pattern: "*"
input: input:
tuple val(sample_id), path(fname) path fname
output: output:
path fname path fname
""" """
echo "Writing output files" echo "Writing output files"
echo $fname
""" """
} }
process output_report {
publishDir "${params.results_dir}", mode: 'copy', pattern: "*report.html"
input:
path fname
output:
path fname
"""
echo "Copying report"
"""
}
// workflow module // workflow module
workflow pipeline { workflow pipeline {
take: take:
reads reads
ref_genome ref_genome
ref_annotation ref_annotation
report_template
table_template
main: main:
map_sample_ids_cls = {it ->
map_sample_ids_cls = {it ->
/* Harmonize tuples /* Harmonize tuples
output: output:
tuple val(sample_id), path('*.gff') tuple val(sample_id), path('*.gff')
@ -405,9 +373,7 @@ workflow pipeline {
seq_for_transcriptome_build = sample_ids.flatten().combine(Channel.fromPath(params.ref_genome)) seq_for_transcriptome_build = sample_ids.flatten().combine(Channel.fromPath(params.ref_genome))
} }
makeReport(report_template, makeReport(software_versions,
table_template,
software_versions,
workflow_params, workflow_params,
params.denovo, params.denovo,
summariseConcatReads.out.summary summariseConcatReads.out.summary
@ -424,18 +390,22 @@ workflow pipeline {
.join(run_gffcompare.out.gffcmp_dir) .join(run_gffcompare.out.gffcmp_dir)
.join(seq_for_transcriptome_build)) .join(seq_for_transcriptome_build))
if (use_ref_ann){ if (use_ref_ann){
results = preprocess_reads.out.report results = preprocess_reads.out.report
.concat(run_gffcompare.output.gffcmp_dir, .concat(run_gffcompare.output.gffcmp_dir,
m.stats, m.stats,
get_transcriptome.out.flatMap(map_sample_ids_cls), get_transcriptome.out.flatMap(map_sample_ids_cls))
) .map {it -> it[1]}
} .concat(makeReport.out.report)
}
if (!use_ref_ann && !params.denovo){ if (!use_ref_ann && !params.denovo){
results = preprocess_reads.out.report results = preprocess_reads.out.report
.concat(m.stats, .concat(m.stats,
get_transcriptome.out.flatMap(map_sample_ids_cls), get_transcriptome.out.flatMap(map_sample_ids_cls))
) .map {it -> it[1]}
.concat(makeReport.out.report)
} }
if (params.denovo){ if (params.denovo){
results = m.cds results = m.cds
@ -443,18 +413,19 @@ workflow pipeline {
seq_for_transcriptome_build, seq_for_transcriptome_build,
get_transcriptome.out.flatMap(map_sample_ids_cls), get_transcriptome.out.flatMap(map_sample_ids_cls),
merge_gff_bundles.out.gff, merge_gff_bundles.out.gff,
m.opt_qual_ch.flatMap {it -> m.opt_qual_ch.flatMap {it ->
l = [] l = []
for (x in it[1..-1]){ for (x in it[1..-1]){
l.add(tuple(it[0], x)) l.add(tuple(it[0], x))
} }
return l return l
}) })
.map {it -> it[1]}
.concat(makeReport.out.report)
} }
emit: emit:
results results
report
telemetry = workflow_params telemetry = workflow_params
} }
@ -463,10 +434,6 @@ WorkflowMain.initialise(workflow, params, log)
workflow { workflow {
start_ping() start_ping()
params.results_dir = "${params.out_dir}/output"
report_template = file("$projectDir/bin/report_template.html")
table_template = file("$projectDir/bin/table_template.html")
fastq = file(params.fastq, type: "file") fastq = file(params.fastq, type: "file")
@ -510,10 +477,9 @@ workflow {
params.fastq, params.out_dir, params.sample, params.sample_sheet, params.sanitize_fastq params.fastq, params.out_dir, params.sample, params.sample_sheet, params.sanitize_fastq
) )
pipeline(reads, ref_genome, ref_annotation, report_template, table_template) pipeline(reads, ref_genome, ref_annotation)
output(pipeline.out.results) output(pipeline.out.results)
output_report(pipeline.out.report)
end_ping(pipeline.out.telemetry) end_ping(pipeline.out.telemetry)
} }

View File

@ -15,17 +15,15 @@ params {
fastq = null fastq = null
ref_genome = false ref_genome = false
ref_annotation = null ref_annotation = null
// Process cDNA reads using pychopper, turn off for direct RNA:
use_pychopper = true
threads = 4 threads = 4
// Thresholds for viewing isoforms in report table // Thresholds for viewing isoforms in report table
transcript_table_cov_thresh = 50 transcript_table_cov_thresh = 50
out_dir = null out_dir = "output"
sample = null sample = null
sample_sheet = null sample_sheet = null
sanitize_fastq = false sanitize_fastq = false
wfversion = "v0.1.0" wfversion = "v0.1.1"
aws_image_prefix = null aws_image_prefix = null
aws_queue = null aws_queue = null
report_name = "report" report_name = "report"

View File

@ -61,11 +61,6 @@
"type": "integer", "type": "integer",
"default": 4 "default": 4
}, },
"use_pychopper": {
"type": "boolean",
"description": "Use pychopper to preprcess reads",
"default": true
},
"pychopper_opts": { "pychopper_opts": {
"type": "string", "type": "string",
"description": "Extra pychopper opts", "description": "Extra pychopper opts",

View File

@ -24,8 +24,8 @@ process map_reads{
minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} ${index} "reads.fa"\ minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} ${index} "reads.fa"\
| samtools view -q ${params.minimum_mapping_quality} -F 2304 -Sb -\ | samtools view -q ${params.minimum_mapping_quality} -F 2304 -Sb -\
| seqkit bam -j ${params.threads} -x -T '${ContextFilter}' -\ | seqkit bam -j ${params.threads} -x -T '${ContextFilter}' -\
| samtools sort -@ ${params.threads} -o "${sample_id}_reads_aln_sorted.bam" - \ | samtools sort -@ ${params.threads} -o "${sample_id}_reads_aln_sorted.bam" - ;
| ((seqkit bam -s -j ${params.threads} - 2>&1) | tee ${sample_id}_read_aln_stats.tsv ) || true ((cat "${sample_id}_reads_aln_sorted.bam" | seqkit bam -s -j ${params.threads} - 2>&1) | tee ${sample_id}_read_aln_stats.tsv ) || true
if [[ -s "internal_priming_fail.tsv" ]]; if [[ -s "internal_priming_fail.tsv" ]];
then then