From f615d8ba54b82641d83a5d94cb2da711106651ec Mon Sep 17 00:00:00 2001
From: piplus2
Date: Mon, 14 Sep 2026 09:43:12 +0200
Subject: [PATCH 1/4] port module isoformswitchanalyzer to nf-core structure
Add environment.yml, meta.yml, a stub and nf-tests for the local
ISOFORMSWITCHANALYZER module, and update IsoformSwitchAnalyzeR 2.2.0 -> 2.12.0
(R 4.3 -> 4.5). bin/run_isoformswitchanalyzer.R becomes a module template
and writes versions.yml itself, replacing the two eval emits.
The tests take the four Salmon quant directories from
testdata/salmon_quant/salmon_quant.tar.gz and reference/genes_chrX.gtf. The
test-datasets carry no transcript fasta, so the setup block extracts one from
reference/X.fa.gz with GUNZIP and GFFREAD -w, as the pipeline does with
rsem-prepare-reference. Three cases: a real run, a run with a dIF cutoff no
isoform reaches, which covers the fallback path the pipeline relies on, and a
stub run. The summary is snapshotted by content, the features table by
header and row count as its DEXSeq statistics are not reproducible across
machines, the plots and the RDS by name.
Also:
- Fix the common switch consequences plot, which was written to
resultscommon_switch_consequences.pdf next to the results directory
instead of inside it, so it was never published.
- With 2.12.0 the chrX test data yields a real switch (RPL10) at the default
alpha, where 2.2.0 found nothing, so the default and salmon_results
pipeline snapshots now carry the switch plots and a real summary. The
features table is added to tests/.nftignore: it depends on Salmon's
non-reproducible quantification and on DEXSeq.
Closes #280
Co-Authored-By: Claude Opus 5 (1M context)
Claude-Session: https://claude.ai/code/session_012ZQZJtuunSpdHWJgAys59c
---
CHANGELOG.md | 3 +
docs/output.md | 3 +-
.../isoformswitchanalyzer/environment.yml | 7 +
modules/local/isoformswitchanalyzer/main.nf | 48 +++--
modules/local/isoformswitchanalyzer/meta.yml | 116 ++++++++++++
.../templates}/run_isoformswitchanalyzer.R | 72 +++++---
.../isoformswitchanalyzer/tests/main.nf.test | 165 ++++++++++++++++++
.../tests/main.nf.test.snap | 109 ++++++++++++
.../tests/nextflow.config | 11 ++
tests/.nftignore | 3 +
tests/default.nf.test.snap | 16 +-
tests/salmon_results.nf.test.snap | 16 +-
12 files changed, 510 insertions(+), 59 deletions(-)
create mode 100644 modules/local/isoformswitchanalyzer/environment.yml
create mode 100644 modules/local/isoformswitchanalyzer/meta.yml
rename {bin => modules/local/isoformswitchanalyzer/templates}/run_isoformswitchanalyzer.R (79%)
mode change 100755 => 100644
create mode 100644 modules/local/isoformswitchanalyzer/tests/main.nf.test
create mode 100644 modules/local/isoformswitchanalyzer/tests/main.nf.test.snap
create mode 100644 modules/local/isoformswitchanalyzer/tests/nextflow.config
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4b5431d..41e8621b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #272 - Add a `test_leafcutter` profile and a pipeline level nf-test for `--leafcutter`, which had no test coverage at all (by @piplus2)
- #272 - Add the `STRAND_JUNCTIONS` module, which gives the junctions of an unstranded sample a strand from the splice motif and the annotation (by @piplus2)
- #264 - Add an nf-test for the `DEXSEQ_DTU` module, which had no test coverage (by @piplus2)
+- #280 - Add an nf-test for the `ISOFORMSWITCHANALYZER` module, which had no test coverage (by @piplus2)
### Changed
@@ -53,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #268 - Move the `EDGER_DEU` subworkflow to the nf-core subworkflow template. It now emits the `EDGER_EXON` results and the featureCounts tables, which it ran but discarded (by @piplus2)
- #270 - Move the `LEAFCUTTER` subworkflow to the nf-core subworkflow template. Its `juncs` output is now the per sample `[ meta, junc ]` tuples, instead of a single list flattening the meta maps in with the paths (by @piplus2)
- #272 - `--leafcutter` now works with `--source genome_bam`, whatever the strandedness, since the junction strand no longer has to come from the alignment. For unstranded libraries the clusters differ slightly from LeafCutter's documented STAR route, see the LeafCutter section of `docs/usage.md` (by @piplus2)
+- #280 - Refactor `ISOFORMSWITCHANALYZER` to the nf-core module template and update `IsoformSwitchAnalyzeR` 2.2.0 -> 2.12.0 (R 4.3 -> 4.5). `bin/run_isoformswitchanalyzer.R` is now a module template (by @piplus2)
### Fixed
@@ -93,6 +95,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #272 - Fix `--leafcutter` clustering nothing. regtools now takes the junction strand from the samplesheet `strandedness`, and the junctions of an unstranded sample are stranded afterwards from the splice motif and the annotation, so the BAM files the other analyses read are left alone (by @piplus2)
- #275 - Fix missing yaml from singularity container in `GTFGENEFILTER` (reported by @srira25, fix by @piplus2)
- #277 - Fix missing pyyaml from singularity container in `STRAND_JUNCTIONS`, `CLUSTERGROUPS` and `MISOPY_SETTINGS` (by @piplus2)
+- #280 - Fix `ISOFORMSWITCHANALYZER` writing `common_switch_consequences.pdf` next to the `results` directory instead of inside it, so it was never published (by @piplus2)
## v1.0.5 - 2024-11-03
diff --git a/docs/output.md b/docs/output.md
index 86c1b9f5..28ba7560 100644
--- a/docs/output.md
+++ b/docs/output.md
@@ -413,13 +413,14 @@ Finally, this portion of the pipeline will run [stageR](https://bioconductor.org
- `02_switch_plot_gene5.pdf`
- `03_switch_plot_gene6.pdf`
- `...`
+ - `common_switch_consequences.pdf`
- `isoformswitchanalyzer_isoformfeatures.csv`
- `isoformswitchanalyzer_summary.csv`
- `switchlist.rds`
-If [IsoformSwitchAnalyzeR](https://www.bioconductor.org/packages/release/bioc/html/IsoformSwitchAnalyzeR.html) finds genes with isoform switches, it produces plots visualizing these switches. A separate set of switch plots is created for each contrast if that contrast contains significant switches. Further two .csv files are created, a summary and the main results. The .rds of the main R list is also returned.
+If [IsoformSwitchAnalyzeR](https://www.bioconductor.org/packages/release/bioc/html/IsoformSwitchAnalyzeR.html) finds genes with isoform switches, it produces plots visualizing these switches. A separate set of switch plots is created for each contrast if that contrast contains significant switches, and `common_switch_consequences.pdf` summarises the consequences of the switches across all contrasts. Further two .csv files are created, a summary and the main results. The .rds of the main R list is also returned.
## Event-based analysis
diff --git a/modules/local/isoformswitchanalyzer/environment.yml b/modules/local/isoformswitchanalyzer/environment.yml
new file mode 100644
index 00000000..e42fad17
--- /dev/null
+++ b/modules/local/isoformswitchanalyzer/environment.yml
@@ -0,0 +1,7 @@
+---
+# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json
+channels:
+ - conda-forge
+ - bioconda
+dependencies:
+ - "bioconda::bioconductor-isoformswitchanalyzer=2.12.0"
diff --git a/modules/local/isoformswitchanalyzer/main.nf b/modules/local/isoformswitchanalyzer/main.nf
index 92249de8..4f2d9d0d 100644
--- a/modules/local/isoformswitchanalyzer/main.nf
+++ b/modules/local/isoformswitchanalyzer/main.nf
@@ -1,37 +1,47 @@
process ISOFORMSWITCHANALYZER {
label 'process_medium'
- conda "bioconda::bioconductor-isoformswitchanalyzer==2.2.0"
- container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
- 'https://depot.galaxyproject.org/singularity/bioconductor-isoformswitchanalyzer:2.2.0--r43ha9d7317_0' :
- 'biocontainers/bioconductor-isoformswitchanalyzer:2.2.0--r43ha9d7317_0' }"
+ conda "${moduleDir}/environment.yml"
+ container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container
+ ? 'https://depot.galaxyproject.org/singularity/bioconductor-isoformswitchanalyzer:2.12.0--r45hd2fad28_0'
+ : 'biocontainers/bioconductor-isoformswitchanalyzer:2.12.0--r45hd2fad28_0'}"
input:
- path salmon_output
- path gtf // path to gtf file
- path transcript_sequences // path to isoform nt sequences fasta
- path samplesheet // path samplesheet
- path contrastsheet // path contrastsheet
- val alpha // alpha value for differential isoform expression
- val dIF // dIF cutoff value for differential isoform expression
+ path salmon_output // path: one Salmon quant directory per sample
+ path gtf // path: /path/to/genes.gtf
+ path transcript_sequences // path: /path/to/transcripts.fa (isoform nucleotide sequences)
+ path samplesheet // path: /path/to/samplesheet.csv
+ path contrastsheet // path: /path/to/contrastsheet.csv
+ val alpha // val: FDR cutoff for the isoform switch test
+ val dIF // val: minimum absolute difference in isoform fraction
output:
- path "isoformswitchanalyzer_summary.csv" , emit: isoformswitchanalyzer_summary
- path "isoformswitchanalyzer_isoformfeatures.csv" , emit: isoformswitchanalyzer_isoformFeatures
- path "switchlist.rds" , emit: switchlist_rds
- path "results" , emit: results
- tuple val("${task.process}"), val('r-base'), eval('R --version 2>&1 | head -n 1 | sed "s/^.*version //; s/ .*$//"'), topic: versions, emit: versions_R
- tuple val("${task.process}"), val('bioconductor-isoformswitchanalyzer'), eval('Rscript -e "library(IsoformSwitchAnalyzeR); cat(as.character(packageVersion(\'IsoformSwitchAnalyzeR\')))"'), topic: versions, emit: versions_isoformswitchanalyzer
-
+ path "isoformswitchanalyzer_summary.csv" , emit: isoformswitchanalyzer_summary
+ path "isoformswitchanalyzer_isoformfeatures.csv", emit: isoformswitchanalyzer_isoformFeatures
+ path "switchlist.rds" , emit: switchlist_rds
+ path "results" , emit: results
+ path "versions.yml" , topic: versions, emit: versions_isoformswitchanalyzer
when:
task.ext.when == null || task.ext.when
script:
+ template 'run_isoformswitchanalyzer.R'
+
+ stub:
def args = task.ext.args ?: ''
"""
+ echo ${args}
+
mkdir -p results
+ touch isoformswitchanalyzer_summary.csv
+ touch isoformswitchanalyzer_isoformfeatures.csv
+ touch switchlist.rds
- run_isoformswitchanalyzer.R ${gtf} ${transcript_sequences} ${samplesheet} ${contrastsheet} ${alpha} ${dIF} ${args}
+ cat <<-END_VERSIONS > versions.yml
+ "${task.process}":
+ r-base: \$(R --version 2>&1 | head -n 1 | sed 's/^.*version //; s/ .*\$//')
+ bioconductor-isoformswitchanalyzer: \$(Rscript -e "library(IsoformSwitchAnalyzeR); cat(as.character(packageVersion('IsoformSwitchAnalyzeR')))")
+ END_VERSIONS
"""
}
diff --git a/modules/local/isoformswitchanalyzer/meta.yml b/modules/local/isoformswitchanalyzer/meta.yml
new file mode 100644
index 00000000..3e4f35fe
--- /dev/null
+++ b/modules/local/isoformswitchanalyzer/meta.yml
@@ -0,0 +1,116 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json
+name: "isoformswitchanalyzer"
+description: Identify isoform switches between conditions from Salmon quantifications
+ with IsoformSwitchAnalyzeR, predict their functional consequences and plot the top
+ switching genes of every contrast
+keywords:
+ - IsoformSwitchAnalyzeR
+ - isoform switch
+ - differential transcript usage
+ - alternative splicing
+ - salmon
+tools:
+ - "IsoformSwitchAnalyzeR":
+ description: "Analysis of alternative splicing and isoform switches with predicted
+ functional consequences (e.g. gain/loss of protein domains etc.) from quantification
+ of all types of RNASeq by tools such as Kallisto, Salmon, StringTie, Cufflinks/Cuffdiff
+ etc."
+ homepage: "https://bioconductor.org/packages/IsoformSwitchAnalyzeR"
+ documentation: "https://bioconductor.org/packages/release/bioc/vignettes/IsoformSwitchAnalyzeR/inst/doc/IsoformSwitchAnalyzeR.html"
+ tool_dev_url: "https://github.com/kvittingseerup/IsoformSwitchAnalyzeR"
+ doi: "10.1093/bioinformatics/btz247"
+ licence: ["GPL-2.0-or-later"]
+ identifier: biotools:isoformswitchanalyzer
+
+input:
+ - salmon_output:
+ type: directory
+ description: One Salmon quant directory per sample, each holding a `quant.sf`.
+ The directory names must match the `sample` column of the samplesheet
+ pattern: "*"
+ - gtf:
+ type: file
+ description: GTF annotation the transcripts were quantified against
+ pattern: "*.gtf"
+ ontologies:
+ - edam: "http://edamontology.org/format_2306" # GTF
+ - transcript_sequences:
+ type: file
+ description: Nucleotide sequences of the annotated transcripts, named by
+ transcript id as in the GTF
+ pattern: "*.{fa,fasta}"
+ ontologies:
+ - edam: "http://edamontology.org/format_1929" # FASTA
+ - samplesheet:
+ type: file
+ description: Comma-separated sample sheet with at least `sample` and `condition`
+ column headers
+ pattern: "*.csv"
+ ontologies:
+ - edam: "http://edamontology.org/format_3752" # CSV
+ - contrastsheet:
+ type: file
+ description: Comma-separated contrast sheet with `treatment` and `control`
+ column headers naming the conditions to compare. Every pair of conditions
+ is compared when the file does not exist
+ pattern: "*.csv"
+ ontologies:
+ - edam: "http://edamontology.org/format_3752" # CSV
+ - alpha:
+ type: float
+ description: FDR cutoff below which an isoform switch is called significant
+ - dIF:
+ type: float
+ description: Minimum absolute difference in isoform fraction between the two
+ conditions for an isoform switch to be called
+
+output:
+ isoformswitchanalyzer_summary:
+ - "isoformswitchanalyzer_summary.csv":
+ type: file
+ description: Number of switching isoforms, switches and genes per contrast
+ and combined. Holds the error message instead when no switch was found
+ pattern: "isoformswitchanalyzer_summary.csv"
+ ontologies:
+ - edam: "http://edamontology.org/format_3752" # CSV
+ isoformswitchanalyzer_isoformFeatures:
+ - "isoformswitchanalyzer_isoformfeatures.csv":
+ type: file
+ description: The `isoformFeatures` table of the switchAnalyzeRlist with the
+ expression, isoform fraction, switch q-value and consequences of every
+ isoform in every contrast. Empty when no switch was found
+ pattern: "isoformswitchanalyzer_isoformfeatures.csv"
+ ontologies:
+ - edam: "http://edamontology.org/format_3752" # CSV
+ switchlist_rds:
+ - "switchlist.rds":
+ type: file
+ description: Serialised switchAnalyzeRlist object
+ pattern: "switchlist.rds"
+ results:
+ - "results":
+ type: directory
+ description: One subdirectory per contrast with a switch plot PDF per switching
+ gene, plus the common switch consequences PDF
+ pattern: "results"
+ versions_isoformswitchanalyzer:
+ - "versions.yml":
+ type: file
+ description: File containing software versions
+ pattern: "versions.yml"
+ ontologies:
+ - edam: "http://edamontology.org/format_3750" # YAML
+
+topics:
+ versions:
+ - "versions.yml":
+ type: file
+ description: File containing software versions
+ pattern: "versions.yml"
+ ontologies:
+ - edam: "http://edamontology.org/format_3750" # YAML
+authors:
+ - "@bensouthgate"
+ - "@piplus2"
+maintainers:
+ - "@piplus2"
diff --git a/bin/run_isoformswitchanalyzer.R b/modules/local/isoformswitchanalyzer/templates/run_isoformswitchanalyzer.R
old mode 100755
new mode 100644
similarity index 79%
rename from bin/run_isoformswitchanalyzer.R
rename to modules/local/isoformswitchanalyzer/templates/run_isoformswitchanalyzer.R
index d0d29013..b6e256a9
--- a/bin/run_isoformswitchanalyzer.R
+++ b/modules/local/isoformswitchanalyzer/templates/run_isoformswitchanalyzer.R
@@ -1,4 +1,5 @@
#!/usr/bin/env Rscript
+
# Scripts adjusted from Bioconductor and IsoformSwitchAnalyzeR source code
# Please see following for details:
# https://bioconductor.org/packages/devel/bioc/vignettes/IsoformSwitchAnalyzeR/inst/doc/IsoformSwitchAnalyzeR.html
@@ -6,23 +7,29 @@
# https://github.com/kvittingseerup/IsoformSwitchAnalyzeR
# respectivly
-# Parse command arguments
+# NOTE: This file is a Nextflow template. Nextflow interpolates the process
+# variables and treats the backslash as an escape character, therefore
+# R's list/data.frame operator must be written as a backslash followed
+# by a dollar sign, and every literal backslash must be doubled.
+
-argv <- commandArgs(trailingOnly = TRUE)
+# Read the process inputs
-salmon_output <- getwd()
+salmon_output <- getwd() # every Salmon quant directory is staged here
-gtf <- argv[1]
+gtf <- '$gtf'
-transcript_sequences <- argv[2]
+transcript_sequences <- '$transcript_sequences'
-samplesheet <- argv[3]
+samplesheet <- '$samplesheet'
-contrastsheet <- argv[4]
+contrastsheet <- '$contrastsheet'
-alpha <- as.numeric(argv[5]) # Must be >= 0 and <= 1
+alpha <- as.numeric('$alpha') # Must be >= 0 and <= 1
-dIFcutoff <- as.numeric(argv[6]) # Must be >= 0 and <= 1
+dIFcutoff <- as.numeric('$dIF') # Must be >= 0 and <= 1
+
+pathToOutput <- 'results'
# Attach required packages
@@ -42,7 +49,7 @@ isoformSwitchAnalysisPart1 <- function(
alpha = NULL,
dIFcutoff = NULL
) {
- isConditional <- switchAnalyzeRlist$sourceId != 'preDefinedSwitches'
+ isConditional <- switchAnalyzeRlist\$sourceId != 'preDefinedSwitches'
# preFilter
if(isConditional) {
@@ -60,7 +67,7 @@ isoformSwitchAnalysisPart1 <- function(
# Test isoform switches
if(isConditional) {
- if(any( switchAnalyzeRlist$conditions$nrReplicates > 5)) {
+ if(any( switchAnalyzeRlist\$conditions\$nrReplicates > 5)) {
switchAnalyzeRlist <-
isoformSwitchTestSatuRn(
switchAnalyzeRlist,
@@ -81,7 +88,7 @@ isoformSwitchAnalysisPart1 <- function(
)
}
- if (nrow(switchAnalyzeRlist$isoformSwitchAnalysis) == 0) {
+ if (nrow(switchAnalyzeRlist\$isoformSwitchAnalysis) == 0) {
stop('No isoform switches were identified with the current cutoffs.')
}
}
@@ -89,7 +96,7 @@ isoformSwitchAnalysisPart1 <- function(
# Predict ORF
- if ( is.null(switchAnalyzeRlist$orfAnalysis) ) {
+ if ( is.null(switchAnalyzeRlist\$orfAnalysis) ) {
# Add known annoation
@@ -101,7 +108,7 @@ isoformSwitchAnalysisPart1 <- function(
# Predict novel once (if any are missing)
- if ( any( switchAnalyzeRlist$orfAnalysis$orf_origin == 'not_annotated_yet' )) {
+ if ( any( switchAnalyzeRlist\$orfAnalysis\$orf_origin == 'not_annotated_yet' )) {
switchAnalyzeRlist <- analyzeNovelIsoformORF(
switchAnalyzeRlist = switchAnalyzeRlist,
analysisAllIsoformsWithoutORF = TRUE,
@@ -182,11 +189,7 @@ isoformSwitchAnalysisPart2 <- function(
# Make overall consequences
pdf(
- file = paste(
- pathToOutput,
- 'common_switch_consequences.pdf',
- sep = ''
- ),
+ file = file.path(pathToOutput, 'common_switch_consequences.pdf'),
width = 10,
height = 7
)
@@ -249,6 +252,11 @@ isoformSwitchAnalysisCombined <- function(
}
+# Create the output directory
+
+dir.create(pathToOutput, showWarnings = FALSE)
+
+
# Load Salmon output
salmonQuant <- importIsoformExpression(
@@ -287,8 +295,8 @@ if (file.exists(contrastsheet)) {
# Build swtich list
SwitchList <- importRdata(
- isoformCountMatrix = salmonQuant$counts,
- isoformRepExpression = salmonQuant$abundance,
+ isoformCountMatrix = salmonQuant\$counts,
+ isoformRepExpression = salmonQuant\$abundance,
designMatrix = design,
isoformExonAnnoation = gtf,
isoformNtFasta = transcript_sequences,
@@ -305,7 +313,7 @@ tryCatch({
SwitchList <- isoformSwitchAnalysisCombined(
SwitchList,
pathToGTF = gtf,
- pathToOutput = "results",
+ pathToOutput = pathToOutput,
alpha = alpha,
dIFcutoff = dIFcutoff
)
@@ -322,7 +330,7 @@ tryCatch({
# Save isoformFeatures as csv
- write.csv(SwitchList$isoformFeatures, "isoformswitchanalyzer_isoformfeatures.csv")
+ write.csv(SwitchList\$isoformFeatures, "isoformswitchanalyzer_isoformfeatures.csv")
}, error = function(e) {
@@ -348,10 +356,20 @@ tryCatch({
saveRDS(SwitchList, "switchlist.rds")
-####################################
-########### Session info ###########
-####################################
+# Save the software versions to disk
+
+writeLines(
+ c(
+ '"${task.process}":',
+ paste0(" r-base: ", paste(R.version[["major"]], R.version[["minor"]], sep = ".")),
+ paste0(" bioconductor-isoformswitchanalyzer: ", as.character(packageVersion("IsoformSwitchAnalyzeR")))
+ ),
+ "versions.yml"
+)
+
+
+# Print session information
-# Print sessioninfo to standard out
citation("IsoformSwitchAnalyzeR")
+
sessionInfo()
diff --git a/modules/local/isoformswitchanalyzer/tests/main.nf.test b/modules/local/isoformswitchanalyzer/tests/main.nf.test
new file mode 100644
index 00000000..8005b520
--- /dev/null
+++ b/modules/local/isoformswitchanalyzer/tests/main.nf.test
@@ -0,0 +1,165 @@
+// nf-core modules test isoformswitchanalyzer
+nextflow_process {
+
+ name "Test Process ISOFORMSWITCHANALYZER"
+ script "../main.nf"
+ process "ISOFORMSWITCHANALYZER"
+ config "./nextflow.config"
+
+ tag "modules"
+ tag "modules_local"
+ tag "isoformswitchanalyzer"
+ tag "untar"
+ tag "gunzip"
+ tag "gffread"
+
+ // The salmon_quant tarball holds one quant directory per sample (ERR188383,
+ // ERR188428, ERR188454, ERR204916), quantified against reference/genes_chrX.gtf.
+ // The test-datasets carry no transcript fasta, so it is extracted from the
+ // chrX genome with gffread -w, as the pipeline does with rsem-prepare-reference
+ setup {
+ run("UNTAR", alias: "UNTAR_SALMON_QUANT") {
+ script '../../../nf-core/untar/main.nf'
+ process {
+ """
+ input[0] = [
+ [ id: 'test' ],
+ file(params.pipelines_testdata_base_path + 'testdata/salmon_quant/salmon_quant.tar.gz', checkIfExists: true)
+ ]
+ """
+ }
+ }
+
+ run("GUNZIP") {
+ script '../../../nf-core/gunzip/main.nf'
+ process {
+ """
+ input[0] = [
+ [ id: 'X' ],
+ file(params.pipelines_testdata_base_path + 'reference/X.fa.gz', checkIfExists: true)
+ ]
+ """
+ }
+ }
+
+ run("GFFREAD") {
+ script '../../../nf-core/gffread/main.nf'
+ process {
+ """
+ input[0] = [
+ [ id: 'genes_chrX' ],
+ file(params.pipelines_testdata_base_path + 'reference/genes_chrX.gtf', checkIfExists: true)
+ ]
+ input[1] = GUNZIP.out.gunzip.map { _meta, fasta -> fasta }
+ """
+ }
+ }
+ }
+
+ test("human chrX - salmon quant") {
+
+ when {
+ process {
+ """
+ input[0] = UNTAR_SALMON_QUANT.out.untar
+ .map { _meta, dir -> file(dir.toString() + '/ERR*', type: 'dir', checkIfExists: true) }
+ input[1] = file(params.pipelines_testdata_base_path + 'reference/genes_chrX.gtf', checkIfExists: true)
+ input[2] = GFFREAD.out.gffread_fasta.map { _meta, fasta -> fasta }
+ input[3] = file(params.pipelines_testdata_base_path + 'samplesheet/samplesheet.csv', checkIfExists: true)
+ input[4] = file(params.pipelines_testdata_base_path + 'samplesheet/contrastsheet.csv', checkIfExists: true)
+ input[5] = 0.05
+ input[6] = 0.1
+ """
+ }
+ }
+
+ then {
+ // The switch summary only holds counts, so it is snapshotted by content.
+ // The isoform features table carries the DEXSeq statistics, which are not
+ // reproducible across machines, so it is snapshotted by shape: header and
+ // row count. The plots are listed by name, one per switching gene and
+ // contrast, and the binary object only by name
+ assert process.success
+ assertAll(
+ { assert snapshot(
+ process.out.isoformswitchanalyzer_summary,
+ process.out.isoformswitchanalyzer_isoformFeatures.collect { csv ->
+ def lines = path(csv).readLines()
+ [ file(csv).name, lines[0], lines.size() ]
+ },
+ process.out.switchlist_rds.collect { rds -> file(rds).name },
+ process.out.results.collect { dir ->
+ getAllFilesFromDir(dir, relative: true)
+ },
+ process.out.findAll { key, _val -> key.startsWith('versions') }
+ ).match() }
+ )
+ }
+
+ }
+
+ test("human chrX - salmon quant - no switches") {
+
+ // A dIF cutoff no isoform reaches: the analysis stops after the switch test,
+ // the summary holds the error message and the features table is empty
+ when {
+ process {
+ """
+ input[0] = UNTAR_SALMON_QUANT.out.untar
+ .map { _meta, dir -> file(dir.toString() + '/ERR*', type: 'dir', checkIfExists: true) }
+ input[1] = file(params.pipelines_testdata_base_path + 'reference/genes_chrX.gtf', checkIfExists: true)
+ input[2] = GFFREAD.out.gffread_fasta.map { _meta, fasta -> fasta }
+ input[3] = file(params.pipelines_testdata_base_path + 'samplesheet/samplesheet.csv', checkIfExists: true)
+ input[4] = file(params.pipelines_testdata_base_path + 'samplesheet/contrastsheet.csv', checkIfExists: true)
+ input[5] = 0.05
+ input[6] = 0.99
+ """
+ }
+ }
+
+ then {
+ assert process.success
+ assertAll(
+ { assert snapshot(
+ process.out.isoformswitchanalyzer_summary,
+ process.out.isoformswitchanalyzer_isoformFeatures,
+ process.out.switchlist_rds.collect { rds -> file(rds).name },
+ process.out.results.collect { dir ->
+ getAllFilesFromDir(dir, relative: true)
+ },
+ process.out.findAll { key, _val -> key.startsWith('versions') }
+ ).match() }
+ )
+ }
+
+ }
+
+ test("human chrX - salmon quant - stub") {
+
+ options "-stub"
+
+ when {
+ process {
+ """
+ input[0] = UNTAR_SALMON_QUANT.out.untar
+ .map { _meta, dir -> file(dir.toString() + '/ERR*', type: 'dir', checkIfExists: true) }
+ input[1] = file(params.pipelines_testdata_base_path + 'reference/genes_chrX.gtf', checkIfExists: true)
+ input[2] = GFFREAD.out.gffread_fasta.map { _meta, fasta -> fasta }
+ input[3] = file(params.pipelines_testdata_base_path + 'samplesheet/samplesheet.csv', checkIfExists: true)
+ input[4] = file(params.pipelines_testdata_base_path + 'samplesheet/contrastsheet.csv', checkIfExists: true)
+ input[5] = 0.05
+ input[6] = 0.1
+ """
+ }
+ }
+
+ then {
+ assert process.success
+ assertAll(
+ { assert snapshot(process.out).match() }
+ )
+ }
+
+ }
+
+}
diff --git a/modules/local/isoformswitchanalyzer/tests/main.nf.test.snap b/modules/local/isoformswitchanalyzer/tests/main.nf.test.snap
new file mode 100644
index 00000000..3435fbea
--- /dev/null
+++ b/modules/local/isoformswitchanalyzer/tests/main.nf.test.snap
@@ -0,0 +1,109 @@
+{
+ "human chrX - salmon quant - stub": {
+ "content": [
+ {
+ "0": [
+ "isoformswitchanalyzer_summary.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
+ ],
+ "1": [
+ "isoformswitchanalyzer_isoformfeatures.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
+ ],
+ "2": [
+ "switchlist.rds:md5,d41d8cd98f00b204e9800998ecf8427e"
+ ],
+ "3": [
+ [
+
+ ]
+ ],
+ "4": [
+ "versions.yml:md5,a912a375db2f9c6bc21da0dd256d253f"
+ ],
+ "isoformswitchanalyzer_isoformFeatures": [
+ "isoformswitchanalyzer_isoformfeatures.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
+ ],
+ "isoformswitchanalyzer_summary": [
+ "isoformswitchanalyzer_summary.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
+ ],
+ "results": [
+ [
+
+ ]
+ ],
+ "switchlist_rds": [
+ "switchlist.rds:md5,d41d8cd98f00b204e9800998ecf8427e"
+ ],
+ "versions_isoformswitchanalyzer": [
+ "versions.yml:md5,a912a375db2f9c6bc21da0dd256d253f"
+ ]
+ }
+ ],
+ "timestamp": "2026-09-14T09:18:56.519609154",
+ "meta": {
+ "nf-test": "0.9.5",
+ "nextflow": "26.04.4"
+ }
+ },
+ "human chrX - salmon quant": {
+ "content": [
+ [
+ "isoformswitchanalyzer_summary.csv:md5,8cb2a864dbf6d26df508842d83a25bdb"
+ ],
+ [
+ [
+ "isoformswitchanalyzer_isoformfeatures.csv",
+ "\"\",\"iso_ref\",\"gene_ref\",\"isoform_id\",\"gene_id\",\"condition_1\",\"condition_2\",\"gene_name\",\"gene_biotype\",\"iso_biotype\",\"gene_overall_mean\",\"gene_value_1\",\"gene_value_2\",\"gene_stderr_1\",\"gene_stderr_2\",\"gene_log2_fold_change\",\"gene_q_value\",\"iso_overall_mean\",\"iso_value_1\",\"iso_value_2\",\"iso_stderr_1\",\"iso_stderr_2\",\"iso_log2_fold_change\",\"iso_q_value\",\"IF_overall\",\"IF1\",\"IF2\",\"dIF\",\"isoform_switch_q_value\",\"gene_switch_q_value\",\"PTC\",\"IR\",\"switchConsequencesGene\"",
+ 185
+ ]
+ ],
+ [
+ "switchlist.rds"
+ ],
+ [
+ [
+ "GBR_vs_YRI/1_switch_plot_RPL10_aka_RPL10.pdf",
+ "YRI_vs_GBR/1_switch_plot_RPL10_aka_RPL10.pdf",
+ "common_switch_consequences.pdf"
+ ]
+ ],
+ {
+ "versions_isoformswitchanalyzer": [
+ "versions.yml:md5,a912a375db2f9c6bc21da0dd256d253f"
+ ]
+ }
+ ],
+ "timestamp": "2026-09-14T09:17:49.937748396",
+ "meta": {
+ "nf-test": "0.9.5",
+ "nextflow": "26.04.4"
+ }
+ },
+ "human chrX - salmon quant - no switches": {
+ "content": [
+ [
+ "isoformswitchanalyzer_summary.csv:md5,4b2ab1b387ded8f479d7cfa96893e93b"
+ ],
+ [
+ "isoformswitchanalyzer_isoformfeatures.csv:md5,a227d42afbcc590b4e949075cde4a5b6"
+ ],
+ [
+ "switchlist.rds"
+ ],
+ [
+ [
+
+ ]
+ ],
+ {
+ "versions_isoformswitchanalyzer": [
+ "versions.yml:md5,a912a375db2f9c6bc21da0dd256d253f"
+ ]
+ }
+ ],
+ "timestamp": "2026-09-14T09:18:40.687092536",
+ "meta": {
+ "nf-test": "0.9.5",
+ "nextflow": "26.04.4"
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/local/isoformswitchanalyzer/tests/nextflow.config b/modules/local/isoformswitchanalyzer/tests/nextflow.config
new file mode 100644
index 00000000..e7860e26
--- /dev/null
+++ b/modules/local/isoformswitchanalyzer/tests/nextflow.config
@@ -0,0 +1,11 @@
+process {
+ withName: ISOFORMSWITCHANALYZER {
+ memory = 6.GB
+ }
+
+ // Extract the spliced transcript sequences from the genome, which is what
+ // ISOFORMSWITCHANALYZER needs as isoform nucleotide fasta
+ withName: GFFREAD {
+ ext.args = '-w'
+ }
+}
diff --git a/tests/.nftignore b/tests/.nftignore
index 24a41cc0..a41d3916 100644
--- a/tests/.nftignore
+++ b/tests/.nftignore
@@ -131,3 +131,6 @@ trimgalore/*fastq.gz_trimming_report.txt
**/*.miso
**/*.pickle
**/*.shelve.*
+
+# IsoformSwitchAnalyzeR
+isoformswitchanalyzer/isoformswitchanalyzer_isoformfeatures.csv
diff --git a/tests/default.nf.test.snap b/tests/default.nf.test.snap
index b88dd93e..7506684b 100644
--- a/tests/default.nf.test.snap
+++ b/tests/default.nf.test.snap
@@ -61,8 +61,8 @@
"gunzip": 1.13
},
"ISOFORMSWITCHANALYZER": {
- "bioconductor-isoformswitchanalyzer": "2.2.0",
- "r-base": "4.3.2"
+ "bioconductor-isoformswitchanalyzer": "2.12.0",
+ "r-base": "4.5.3"
},
"MAKE_TRANSCRIPTS_FASTA": {
"rsem": "1.3.1",
@@ -194,6 +194,11 @@
"isoformswitchanalyzer/isoformswitchanalyzer_isoformfeatures.csv",
"isoformswitchanalyzer/isoformswitchanalyzer_summary.csv",
"isoformswitchanalyzer/results",
+ "isoformswitchanalyzer/results/GBR_vs_YRI",
+ "isoformswitchanalyzer/results/GBR_vs_YRI/1_switch_plot_RPL10_aka_RPL10.pdf",
+ "isoformswitchanalyzer/results/YRI_vs_GBR",
+ "isoformswitchanalyzer/results/YRI_vs_GBR/1_switch_plot_RPL10_aka_RPL10.pdf",
+ "isoformswitchanalyzer/results/common_switch_consequences.pdf",
"isoformswitchanalyzer/switchlist.rds",
"mergeevents",
"mergeevents/genes_chrX.ioe",
@@ -4703,8 +4708,7 @@
"trimgalore/fastqc/ERR204916_2_val_2_fastqc.zip"
],
[
- "isoformswitchanalyzer_isoformfeatures.csv:md5,a227d42afbcc590b4e949075cde4a5b6",
- "isoformswitchanalyzer_summary.csv:md5,4b2ab1b387ded8f479d7cfa96893e93b",
+ "isoformswitchanalyzer_summary.csv:md5,8cb2a864dbf6d26df508842d83a25bdb",
"genes_chrX_genes.gff3:md5,1c1ece4ee637e317c0ae6528a253a83f",
"genes.gff:md5,0ef855fcf03bcb7d49afde3e63577465",
"miso_settings.txt:md5,c092cc3e878a299674a99c508b6b2e72",
@@ -4823,10 +4827,10 @@
"genes_chrX.ioi:md5,6c9ee3567554e3f74186b4c17ec777d6"
]
],
- "timestamp": "2026-08-25T14:33:14.412269311",
+ "timestamp": "2026-09-14T09:39:10.533078728",
"meta": {
"nf-test": "0.9.5",
- "nextflow": "26.04.3"
+ "nextflow": "26.04.4"
}
}
}
diff --git a/tests/salmon_results.nf.test.snap b/tests/salmon_results.nf.test.snap
index 6a590278..e5ecdf73 100644
--- a/tests/salmon_results.nf.test.snap
+++ b/tests/salmon_results.nf.test.snap
@@ -38,8 +38,8 @@
"gunzip": 1.13
},
"ISOFORMSWITCHANALYZER": {
- "bioconductor-isoformswitchanalyzer": "2.2.0",
- "r-base": "4.3.2"
+ "bioconductor-isoformswitchanalyzer": "2.12.0",
+ "r-base": "4.5.3"
},
"MAKE_TRANSCRIPTS_FASTA": {
"rsem": "1.3.1",
@@ -92,6 +92,11 @@
"isoformswitchanalyzer/isoformswitchanalyzer_isoformfeatures.csv",
"isoformswitchanalyzer/isoformswitchanalyzer_summary.csv",
"isoformswitchanalyzer/results",
+ "isoformswitchanalyzer/results/GBR_vs_YRI",
+ "isoformswitchanalyzer/results/GBR_vs_YRI/1_switch_plot_RPL10_aka_RPL10.pdf",
+ "isoformswitchanalyzer/results/YRI_vs_GBR",
+ "isoformswitchanalyzer/results/YRI_vs_GBR/1_switch_plot_RPL10_aka_RPL10.pdf",
+ "isoformswitchanalyzer/results/common_switch_consequences.pdf",
"isoformswitchanalyzer/switchlist.rds",
"mergeevents",
"mergeevents/genes_chrX.ioe",
@@ -273,15 +278,14 @@
"salmon/tximport/tximport.tx2gene.tsv"
],
[
- "isoformswitchanalyzer_isoformfeatures.csv:md5,a227d42afbcc590b4e949075cde4a5b6",
- "isoformswitchanalyzer_summary.csv:md5,4b2ab1b387ded8f479d7cfa96893e93b",
+ "isoformswitchanalyzer_summary.csv:md5,8cb2a864dbf6d26df508842d83a25bdb",
"genes_chrX.ioi:md5,6c9ee3567554e3f74186b4c17ec777d6"
]
],
- "timestamp": "2026-09-07T19:55:52.390384017",
+ "timestamp": "2026-09-14T09:33:02.530765961",
"meta": {
"nf-test": "0.9.5",
- "nextflow": "26.04.1"
+ "nextflow": "26.04.4"
}
}
}
\ No newline at end of file
From 18d0420caec2ec5dfd29ee6df079b2af9d34021a Mon Sep 17 00:00:00 2001
From: piplus2
Date: Mon, 14 Sep 2026 09:47:18 +0200
Subject: [PATCH 2/4] restore skip singularity ci
---
.github/workflows/nf-test.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/nf-test.yml b/.github/workflows/nf-test.yml
index 9d51ba5f..b3ffe5b7 100644
--- a/.github/workflows/nf-test.yml
+++ b/.github/workflows/nf-test.yml
@@ -72,12 +72,12 @@ jobs:
profile: [conda, docker, singularity]
isMain:
- ${{ github.base_ref == 'master' || github.base_ref == 'main' }}
- # Exclude conda and ~singularity~ on dev
+ # Exclude conda and singularity on dev
exclude:
- isMain: false
profile: "conda"
- # - isMain: false
- # profile: "singularity"
+ - isMain: false
+ profile: "singularity"
NXF_VER:
- 26.04.0
- latest-everything
From 407af163e236431d25ccf285595ae142068c9447 Mon Sep 17 00:00:00 2001
From: piplus2
Date: Mon, 14 Sep 2026 10:00:22 +0200
Subject: [PATCH 3/4] fix pr number in changelog
---
CHANGELOG.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41e8621b..9276956e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #272 - Add a `test_leafcutter` profile and a pipeline level nf-test for `--leafcutter`, which had no test coverage at all (by @piplus2)
- #272 - Add the `STRAND_JUNCTIONS` module, which gives the junctions of an unstranded sample a strand from the splice motif and the annotation (by @piplus2)
- #264 - Add an nf-test for the `DEXSEQ_DTU` module, which had no test coverage (by @piplus2)
-- #280 - Add an nf-test for the `ISOFORMSWITCHANALYZER` module, which had no test coverage (by @piplus2)
+- #281 - Add an nf-test for the `ISOFORMSWITCHANALYZER` module, which had no test coverage (by @piplus2)
### Changed
@@ -54,7 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #268 - Move the `EDGER_DEU` subworkflow to the nf-core subworkflow template. It now emits the `EDGER_EXON` results and the featureCounts tables, which it ran but discarded (by @piplus2)
- #270 - Move the `LEAFCUTTER` subworkflow to the nf-core subworkflow template. Its `juncs` output is now the per sample `[ meta, junc ]` tuples, instead of a single list flattening the meta maps in with the paths (by @piplus2)
- #272 - `--leafcutter` now works with `--source genome_bam`, whatever the strandedness, since the junction strand no longer has to come from the alignment. For unstranded libraries the clusters differ slightly from LeafCutter's documented STAR route, see the LeafCutter section of `docs/usage.md` (by @piplus2)
-- #280 - Refactor `ISOFORMSWITCHANALYZER` to the nf-core module template and update `IsoformSwitchAnalyzeR` 2.2.0 -> 2.12.0 (R 4.3 -> 4.5). `bin/run_isoformswitchanalyzer.R` is now a module template (by @piplus2)
+- #281 - Refactor `ISOFORMSWITCHANALYZER` to the nf-core module template and update `IsoformSwitchAnalyzeR` 2.2.0 -> 2.12.0 (R 4.3 -> 4.5). `bin/run_isoformswitchanalyzer.R` is now a module template (by @piplus2)
### Fixed
@@ -95,7 +95,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #272 - Fix `--leafcutter` clustering nothing. regtools now takes the junction strand from the samplesheet `strandedness`, and the junctions of an unstranded sample are stranded afterwards from the splice motif and the annotation, so the BAM files the other analyses read are left alone (by @piplus2)
- #275 - Fix missing yaml from singularity container in `GTFGENEFILTER` (reported by @srira25, fix by @piplus2)
- #277 - Fix missing pyyaml from singularity container in `STRAND_JUNCTIONS`, `CLUSTERGROUPS` and `MISOPY_SETTINGS` (by @piplus2)
-- #280 - Fix `ISOFORMSWITCHANALYZER` writing `common_switch_consequences.pdf` next to the `results` directory instead of inside it, so it was never published (by @piplus2)
+- #281 - Fix `ISOFORMSWITCHANALYZER` writing `common_switch_consequences.pdf` next to the `results` directory instead of inside it, so it was never published (by @piplus2)
## v1.0.5 - 2024-11-03
From 9678c745b56d9cc1f81ae66fae62b84dcb84d3d9 Mon Sep 17 00:00:00 2001
From: piplus2
Date: Mon, 14 Sep 2026 10:06:06 +0200
Subject: [PATCH 4/4] redraw the pipeline metro map with nf-metro
Replace the hand-drawn docs/rnasplice_map.png with an nf-metro map whose
.mmd source is committed next to the rendered SVG in docs/images/. The map
now covers LeafCutter, the --source inputs (genome BAM, transcriptome BAM,
Salmon quant directories) as off-track file icons dropping into the station
they replace, and the bigWig coverage tracks. Four lines: DEU, DTU,
event-based splicing, and QC & visualisation.
Only the star_salmon route to Salmon is drawn, not the direct pseudo-aligner
one from the trimmed reads: every layout carrying both edges either crashed
the router or tangled the alignment section.
Re-render with:
nf-metro render docs/images/nf-core-rnasplice_metro_map.mmd \
-o docs/images/nf-core-rnasplice_metro_map.svg --embed-font
Co-Authored-By: Claude Opus 5 (1M context)
Claude-Session: https://claude.ai/code/session_012ZQZJtuunSpdHWJgAys59c
---
CHANGELOG.md | 1 +
README.md | 2 +-
docs/images/nf-core-rnasplice_metro_map.mmd | 115 +++++++
docs/images/nf-core-rnasplice_metro_map.svg | 337 ++++++++++++++++++++
docs/rnasplice_map.png | Bin 613479 -> 0 bytes
5 files changed, 454 insertions(+), 1 deletion(-)
create mode 100644 docs/images/nf-core-rnasplice_metro_map.mmd
create mode 100644 docs/images/nf-core-rnasplice_metro_map.svg
delete mode 100644 docs/rnasplice_map.png
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9276956e..88e8f786 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #272 - Add the `STRAND_JUNCTIONS` module, which gives the junctions of an unstranded sample a strand from the splice motif and the annotation (by @piplus2)
- #264 - Add an nf-test for the `DEXSEQ_DTU` module, which had no test coverage (by @piplus2)
- #281 - Add an nf-test for the `ISOFORMSWITCHANALYZER` module, which had no test coverage (by @piplus2)
+- #281 - Redraw the pipeline metro map with [nf-metro](https://github.com/seqeralabs/nf-metro), now covering LeafCutter, the `--source` inputs and the coverage tracks. The `.mmd` source lives next to the SVG in `docs/images/` (by @piplus2)
### Changed
diff --git a/README.md b/README.md
index 3abf3182..57c69c26 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@
**nf-core/rnasplice** is a bioinformatics pipeline for alternative splicing analysis of RNA sequencing data obtained from organisms with a reference genome and annotation.
-
+
1. Merge re-sequenced FastQ files ([`cat`](http://www.linfo.org/cat.html))
2. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/))
diff --git a/docs/images/nf-core-rnasplice_metro_map.mmd b/docs/images/nf-core-rnasplice_metro_map.mmd
new file mode 100644
index 00000000..e7fbef3b
--- /dev/null
+++ b/docs/images/nf-core-rnasplice_metro_map.mmd
@@ -0,0 +1,115 @@
+%%metro title: nf-core/rnasplice
+%%metro logo: nf-core-rnasplice_logo_light.png | nf-core-rnasplice_logo_dark.png
+%%metro style: nfcore
+%%metro center_ports: true
+%%metro compact_offsets: true
+%%metro files: fastq_in | FASTQ | Reads
+%%metro file: genome_bam_in | BAM | Genome
+%%metro file: transcriptome_bam_in | BAM | Transcriptome
+%%metro dir: salmon_in | Salmon | Quant
+%%metro file: bigwig_out | BW | Coverage
+%%metro file: report_out | HTML | MultiQC
+%%metro off_track: genome_bam_in, transcriptome_bam_in, salmon_in
+%%metro line: deu | Differential exon usage (DEU) | #e63946
+%%metro line: dtu | Differential transcript usage (DTU) | #4361ee
+%%metro line: event | Event-based splicing | #f39c12
+%%metro line: qc | QC & visualisation | #2db572
+%%metro legend: bl
+%%metro grid: preprocessing | 0,0
+%%metro grid: alignment | 1,0
+%%metro grid: quantification | 1,1
+%%metro grid: exon_analysis | 2,0
+%%metro grid: transcript_analysis | 2,1
+%%metro grid: reporting | 3,0
+
+graph LR
+ subgraph preprocessing [Pre-processing]
+ fastq_in[ ]
+ cat_fastq[cat]
+ fastqc_raw[FastQC]
+ trimgalore[Trim Galore!]
+ fastqc_trim[FastQC]
+
+ fastq_in -->|deu,dtu,event,qc| cat_fastq
+ cat_fastq -->|deu,dtu,event,qc| fastqc_raw
+ fastqc_raw -->|deu,dtu,event,qc| trimgalore
+ trimgalore -->|deu,dtu,event,qc| fastqc_trim
+ end
+
+ subgraph alignment [Genome alignment]
+ genome_bam_in[ ]
+ star[STAR]
+ samtools[SAMtools]
+
+ star -->|deu,event,qc| samtools
+ genome_bam_in -->|deu,event,qc| samtools
+ end
+
+ subgraph quantification [Transcript quantification]
+ transcriptome_bam_in[ ]
+ salmon_in[ ]
+ salmon[Salmon]
+ tximport[tximport]
+
+ transcriptome_bam_in -->|dtu,event| salmon
+ salmon -->|dtu,event| tximport
+ salmon_in -->|dtu,event| tximport
+ end
+
+ subgraph exon_analysis [Exon & junction based analysis]
+ htseq[HTSeq]
+ dexseq_exon[DEXSeq]
+ featurecounts[featureCounts]
+ edger[edgeR]
+ rmats[rMATS]
+ regtools[regtools]
+ leafcutter[LeafCutter]
+
+ htseq -->|deu| dexseq_exon
+ featurecounts -->|deu| edger
+ regtools -->|event| leafcutter
+ end
+
+ subgraph transcript_analysis [Transcript based analysis]
+ drimseq[DRIMSeq]
+ dexseq_dtu[DEXSeq]
+ stager[stageR]
+ isar[IsoformSwitchAnalyzeR]
+ suppa_events[SUPPA events]
+ suppa_psi[SUPPA PSI]
+ suppa_diff[SUPPA diffSplice]
+ suppa_cluster[SUPPA cluster]
+
+ drimseq -->|dtu| dexseq_dtu
+ dexseq_dtu -->|dtu| stager
+ suppa_events -->|event| suppa_psi
+ suppa_psi -->|event| suppa_diff
+ suppa_diff -->|event| suppa_cluster
+ end
+
+ subgraph reporting [Visualisation & reporting]
+ miso[MISO sashimi]
+ bedtools[BEDTools]
+ bigwig[bedGraphToBigWig]
+ multiqc[MultiQC]
+ bigwig_out[ ]
+ report_out[ ]
+
+ bedtools -->|qc| bigwig
+ bigwig -->|qc| bigwig_out
+ multiqc -->|qc| report_out
+ end
+
+ %% Inter-section edges
+ fastqc_trim -->|deu,dtu,event,qc| star
+ star -->|dtu,event| salmon
+ samtools -->|deu| htseq
+ samtools -->|deu| featurecounts
+ samtools -->|event| rmats
+ samtools -->|event| regtools
+ samtools -->|qc| miso
+ samtools -->|qc| bedtools
+ samtools -->|qc| multiqc
+ tximport -->|dtu| drimseq
+ tximport -->|event| suppa_events
+ salmon -->|dtu| isar
diff --git a/docs/images/nf-core-rnasplice_metro_map.svg b/docs/images/nf-core-rnasplice_metro_map.svg
new file mode 100644
index 00000000..7d92bffd
--- /dev/null
+++ b/docs/images/nf-core-rnasplice_metro_map.svg
@@ -0,0 +1,337 @@
+
+
diff --git a/docs/rnasplice_map.png b/docs/rnasplice_map.png
deleted file mode 100644
index ef4855ddb04059b21f76dd54e5112c4a8f41403e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 613479
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