Output dexseq file [CW-6285]

This commit is contained in:
Sarah Griffiths 2025-07-29 08:54:20 +00:00
parent a9e63518e5
commit 62e4266040
7 changed files with 46 additions and 20 deletions

View File

@ -6,20 +6,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Changed ### Changed
- Updated to wf-template v5.6.1, changing: - Updated to wf-template v5.6.2, changing:
- Reduce verbosity of debug logging from fastcat which can occasionally occlude errors found in FASTQ files during ingress. - Reduce verbosity of debug logging from fastcat which can occasionally occlude errors found in FASTQ files during ingress.
- Log banner art to say "EPI2ME" instead of "EPI2ME Labs" to match current branding. This has no effect on the workflow outputs. - Log banner art to say "EPI2ME" instead of "EPI2ME Labs" to match current branding. This has no effect on the workflow outputs.
- pre-commit configuration to resolve an internal dependency problem with flake8. This has no effect on the workflow. - pre-commit configuration to resolve an internal dependency problem with flake8. This has no effect on the workflow.
- Stringtie updated to v2.2.3, which fixes stalling at transcriptome assembly step. - Stringtie updated to v2.2.3, which fixes stalling at transcriptome assembly step.
- Gffcompare updated to v0.12.6, which fixes issue where ref_gene_id was assigned an nan value. - Gffcompare updated to v0.12.6, which fixes issue where ref_gene_id was assigned an nan value.
### Fixed ### Fixed
- Updated to wf-template v5.6.1, fixing: - Updated to wf-template v5.6.2, fixing:
- dacite.exceptions.WrongTypeError during report generation when barcode is null. - dacite.exceptions.WrongTypeError during report generation when barcode is null.
- Sequence summary read length N50 incorrectly displayed minimum read length, it now correctly shows the N50. - Sequence summary read length N50 incorrectly displayed minimum read length, it now correctly shows the N50.
- Sequence summary component alignment and coverage plots failed to plot under some conditions. - Sequence summary component alignment and coverage plots failed to plot under some conditions.
- Error in `deAnalysis` process - `mode(counts) %in% "numeric" is not TRUE` - caused by hyphens in sample sheet aliases. - Error in `deAnalysis` process - `mode(counts) %in% "numeric" is not TRUE` - caused by hyphens in sample sheet aliases.
- Error in `deAnalysis` process - `values in 'transcripts$tx_strand' must be "+" or "-"`. - Error in `deAnalysis` process - `values in 'transcripts$tx_strand' must be "+" or "-"`.
- The workflow will now filter out any unstranded annotations from downstream analysis and log a warning. - The workflow will now filter out any unstranded annotations from downstream analysis and log a warning.
- Output the `results_dexseq.tsv` file when `--de_analysis` enabled.
## [v1.7.0] ## [v1.7.0]
### Changed ### Changed

View File

@ -107,10 +107,11 @@ class TrackBuilder:
if idx_basename == f"{basename}.gzi" and basename.endswith(".gz"): if idx_basename == f"{basename}.gzi" and basename.endswith(".gz"):
self.gzi = ref_index self.gzi = ref_index
def parse_fnames(self, fofn): def parse_fnames(self, fofn, keep_track_order=False):
"""Parse list with filenames and return them grouped. """Parse list with filenames and set self with samples info per track.
:param fofn: File with list of file names (one per line) :param fofn: File with list of file names (one per line)
:param keep_track_order: Keep track order as in the list of file names.
""" """
tmp_samples = {} tmp_samples = {}
with open(fofn, "r") as f: with open(fofn, "r") as f:
@ -134,14 +135,18 @@ class TrackBuilder:
tmp_samples["NO_SAMPLE"] = SampleBundle(sample="NO_SAMPLE") tmp_samples["NO_SAMPLE"] = SampleBundle(sample="NO_SAMPLE")
tmp_samples["NO_SAMPLE"].append(fname) tmp_samples["NO_SAMPLE"].append(fname)
# Re-order samples in dict and add them to the list, leaving # Re-order samples in dict and add them to the list, leaving
# NO_SAMPLE as last # NO_SAMPLE
sorted_samples = ( samples = {
sorted([sample for sample in tmp_samples.keys() if sample != 'NO_SAMPLE']) sample_name: sample_content
) for sample_name, sample_content in tmp_samples.items()
if 'NO_SAMPLE' in tmp_samples.keys(): if sample_name != "NO_SAMPLE"
sorted_samples += ['NO_SAMPLE'] }
for sample in sorted_samples: if not keep_track_order:
self.samples[sample] = tmp_samples[sample] samples = dict(sorted(samples.items()))
# Add NO_SAMPLE as last
if "NO_SAMPLE" in tmp_samples.keys():
samples.update({"NO_SAMPLE": tmp_samples["NO_SAMPLE"]})
self.samples = samples
def build_igv_json(self): def build_igv_json(self):
"""Ensure there is a reference genome.""" """Ensure there is a reference genome."""
@ -171,12 +176,22 @@ class TrackBuilder:
bundle.process_data() bundle.process_data()
# Add the bundled data to the tracks # Add the bundled data to the tracks
for fname, index, file_fmt in bundle.data_bundles: for fname, index, file_fmt in bundle.data_bundles:
# Check if there are custom opts per track
if sample != "NO_SAMPLE" and isinstance(
self.extra_opts_lookups[file_fmt], list
):
sample_info = {}
for e in self.extra_opts_lookups[file_fmt]:
sample_info.update(e)
extra_opts_lookups_track = sample_info[sample]
else:
extra_opts_lookups_track = self.extra_opts_lookups[file_fmt]
self.add_track( self.add_track(
fname, fname,
file_fmt, file_fmt,
sample_name=sample if sample != "NO_SAMPLE" else None, sample_name=sample if sample != "NO_SAMPLE" else None,
index=index, index=index,
extra_opts=self.extra_opts_lookups[file_fmt], extra_opts=extra_opts_lookups_track,
) )
def add_track(self, infile, file_fmt, sample_name=None, index=None, extra_opts={}): def add_track(self, infile, file_fmt, sample_name=None, index=None, extra_opts={}):
@ -325,7 +340,7 @@ def main(args):
) )
# Import files # Import files
igv_builder.parse_fnames(args.fofn) igv_builder.parse_fnames(args.fofn, args.keep_track_order)
# initialise the IGV options dict with the reference options # initialise the IGV options dict with the reference options
igv_builder.build_igv_json() igv_builder.build_igv_json()
@ -335,7 +350,6 @@ def main(args):
igv_builder.add_locus(args.locus) igv_builder.add_locus(args.locus)
json.dump(igv_builder.igv_json, sys.stdout, indent=4) json.dump(igv_builder.igv_json, sys.stdout, indent=4)
logger.info("Printed IGV config JSON to STDOUT.") logger.info("Printed IGV config JSON to STDOUT.")
@ -350,6 +364,11 @@ def argparser():
"(one filename per line)" "(one filename per line)"
), ),
) )
parser.add_argument(
"--keep-track-order",
action="store_true",
help="Keep track order as provided in fofn",
)
parser.add_argument( parser.add_argument(
"--locus", "--locus",
help="Locus string to set initial genomic coordinates to display in IGV", help="Locus string to set initial genomic coordinates to display in IGV",

View File

@ -28,6 +28,7 @@ process configure_igv {
val locus_str val locus_str
val aln_extra_opts val aln_extra_opts
val var_extra_opts val var_extra_opts
val keep_track_order
output: path "igv.json" output: path "igv.json"
script: script:
// the locus argument just makes sure that the initial view in IGV shows something // the locus argument just makes sure that the initial view in IGV shows something
@ -43,6 +44,8 @@ process configure_igv {
var_extra_opts ? new JsonBuilder(var_extra_opts).toPrettyString() : "" var_extra_opts ? new JsonBuilder(var_extra_opts).toPrettyString() : ""
String var_extra_opts_arg = \ String var_extra_opts_arg = \
var_extra_opts ? "--extra-vcf-opts extra-var-opts.json" : "" var_extra_opts ? "--extra-vcf-opts extra-var-opts.json" : ""
String keep_track_order_arg = \
keep_track_order ? "--keep-track-order" : ""
""" """
# write out JSON files with extra options for the alignment and variant tracks # write out JSON files with extra options for the alignment and variant tracks
echo '$aln_opts_json_str' > extra-aln-opts.json echo '$aln_opts_json_str' > extra-aln-opts.json
@ -53,6 +56,7 @@ process configure_igv {
$locus_arg \ $locus_arg \
$aln_extra_opts_arg \ $aln_extra_opts_arg \
$var_extra_opts_arg \ $var_extra_opts_arg \
$keep_track_order_arg \
> igv.json > igv.json
""" """
} }

View File

@ -919,6 +919,7 @@ workflow pipeline {
Channel.of(null), // igv locus Channel.of(null), // igv locus
[displayMode: "SQUISHED", colorBy: "strand"], // bam extra opts [displayMode: "SQUISHED", colorBy: "strand"], // bam extra opts
Channel.of(null), // vcf extra opts Channel.of(null), // vcf extra opts
Channel.of(false), // keep_track_order opts
) )
results = results.concat(igv_conf.map{ [it, null]}) results = results.concat(igv_conf.map{ [it, null]})

View File

@ -94,9 +94,9 @@ params {
"--ref_genome 'wf-transcriptomes-demo/hg38_chr20.fa'", "--ref_genome 'wf-transcriptomes-demo/hg38_chr20.fa'",
"--sample_sheet 'wf-transcriptomes-demo/sample_sheet.csv'", "--sample_sheet 'wf-transcriptomes-demo/sample_sheet.csv'",
] ]
agent = null agent = null
container_sha = "shaaaf20a5a0e76f9e18bad21af639a6b69e4a31a2f" container_sha = "shaaaf20a5a0e76f9e18bad21af639a6b69e4a31a2f"
common_sha = "sha1c69fd30053aad5d516e9567b3944384325a0fee" common_sha = "sha72f3517dd994984e0e2da0b97cb3f23f8540be4b"
} }
} }

View File

@ -318,7 +318,8 @@
}, },
"store_dir": { "store_dir": {
"type": "string", "type": "string",
"hidden": true "hidden": true,
"format" : "directory-path"
}, },
"disable_ping": { "disable_ping": {
"type": "boolean", "type": "boolean",

View File

@ -192,7 +192,7 @@ workflow differential_expression {
analysis.gene_counts, analysis.dge, analysis.dexseq, analysis.gene_counts, analysis.dge, analysis.dexseq,
analysis.stageR, sample_sheet, merged, ref_annotation, merged_TPM, analysis.unflt_counts).collect() analysis.stageR, sample_sheet, merged, ref_annotation, merged_TPM, analysis.unflt_counts).collect()
// Concat files required to be output to user without any changes // Concat files required to be output to user without any changes
de_outputs_concat = analysis.cpm.concat(plotResults.out.dtu_plots, analysis.dge_pdf, analysis.dge_tsv, de_outputs_concat = analysis.cpm.concat(analysis.dexseq, plotResults.out.dtu_plots, analysis.dge_pdf, analysis.dge_tsv,
analysis.dtu_gene, analysis.dtu_transcript, analysis.dtu_stageR, analysis.dtu_pdf, merged_TPM).collect() analysis.dtu_gene, analysis.dtu_transcript, analysis.dtu_stageR, analysis.dtu_pdf, merged_TPM).collect()
collected_de_alignment_stats = mapped.align_stats.collect() collected_de_alignment_stats = mapped.align_stats.collect()
emit: emit: