Merge branch 'CW-6285' into 'dev'

Output dexseq file [CW-6285]

Closes CW-6285

See merge request epi2melabs/workflows/wf-transcriptomes!219
This commit is contained in:
Sarah Griffiths 2025-07-29 08:54:20 +00:00
commit 7a94b2c515
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]
### 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.
- 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.
- 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.
### 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.
- 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.
- 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 "-"`.
- 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]
### Changed

View File

@ -107,10 +107,11 @@ class TrackBuilder:
if idx_basename == f"{basename}.gzi" and basename.endswith(".gz"):
self.gzi = ref_index
def parse_fnames(self, fofn):
"""Parse list with filenames and return them grouped.
def parse_fnames(self, fofn, keep_track_order=False):
"""Parse list with filenames and set self with samples info per track.
: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 = {}
with open(fofn, "r") as f:
@ -134,14 +135,18 @@ class TrackBuilder:
tmp_samples["NO_SAMPLE"] = SampleBundle(sample="NO_SAMPLE")
tmp_samples["NO_SAMPLE"].append(fname)
# Re-order samples in dict and add them to the list, leaving
# NO_SAMPLE as last
sorted_samples = (
sorted([sample for sample in tmp_samples.keys() if sample != 'NO_SAMPLE'])
)
if 'NO_SAMPLE' in tmp_samples.keys():
sorted_samples += ['NO_SAMPLE']
for sample in sorted_samples:
self.samples[sample] = tmp_samples[sample]
# NO_SAMPLE
samples = {
sample_name: sample_content
for sample_name, sample_content in tmp_samples.items()
if sample_name != "NO_SAMPLE"
}
if not keep_track_order:
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):
"""Ensure there is a reference genome."""
@ -171,12 +176,22 @@ class TrackBuilder:
bundle.process_data()
# Add the bundled data to the tracks
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(
fname,
file_fmt,
sample_name=sample if sample != "NO_SAMPLE" else None,
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={}):
@ -325,7 +340,7 @@ def main(args):
)
# 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
igv_builder.build_igv_json()
@ -335,7 +350,6 @@ def main(args):
igv_builder.add_locus(args.locus)
json.dump(igv_builder.igv_json, sys.stdout, indent=4)
logger.info("Printed IGV config JSON to STDOUT.")
@ -350,6 +364,11 @@ def argparser():
"(one filename per line)"
),
)
parser.add_argument(
"--keep-track-order",
action="store_true",
help="Keep track order as provided in fofn",
)
parser.add_argument(
"--locus",
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 aln_extra_opts
val var_extra_opts
val keep_track_order
output: path "igv.json"
script:
// 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() : ""
String var_extra_opts_arg = \
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
echo '$aln_opts_json_str' > extra-aln-opts.json
@ -53,6 +56,7 @@ process configure_igv {
$locus_arg \
$aln_extra_opts_arg \
$var_extra_opts_arg \
$keep_track_order_arg \
> igv.json
"""
}

View File

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

View File

@ -96,7 +96,7 @@ params {
]
agent = null
container_sha = "shaaaf20a5a0e76f9e18bad21af639a6b69e4a31a2f"
common_sha = "sha1c69fd30053aad5d516e9567b3944384325a0fee"
common_sha = "sha72f3517dd994984e0e2da0b97cb3f23f8540be4b"
}
}

View File

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

View File

@ -192,7 +192,7 @@ workflow differential_expression {
analysis.gene_counts, analysis.dge, analysis.dexseq,
analysis.stageR, sample_sheet, merged, ref_annotation, merged_TPM, analysis.unflt_counts).collect()
// 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()
collected_de_alignment_stats = mapped.align_stats.collect()
emit: