diff --git a/modules/nf-core/decoupler/decoupler/main.nf b/modules/nf-core/decoupler/decoupler/main.nf index 7d2ae333f1f7..8d7c5e713b26 100644 --- a/modules/nf-core/decoupler/decoupler/main.nf +++ b/modules/nf-core/decoupler/decoupler/main.nf @@ -1,7 +1,6 @@ process DECOUPLER { tag "$meta.id" label 'process_medium' - conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/dc/dc0ee6d6033b9f04c6377ad3b1cf5f924e3243626ab8d7be836d9d6617f8da4e/data' : @@ -9,13 +8,13 @@ process DECOUPLER { input: tuple val(meta), path(mat) - path(net) - path(gtf) + tuple val(meta2), path(net) + tuple val(meta3), path(annot) output: tuple val(meta), path("*estimate_decoupler.tsv"), emit: dc_estimate tuple val(meta), path("*pvals_decoupler.tsv"), emit: dc_pvals - tuple val(meta), path("*decoupler_plot.png"), emit: png + tuple val(meta), path("*estimate_decoupler_plot.png"), emit: png path("versions.yml"), emit: versions when: @@ -26,9 +25,9 @@ process DECOUPLER { stub: """ - touch ${task.ext.prefix}_estimate_decoupler.tsv - touch ${task.ext.prefix}_pvals_decoupler.tsv - touch ${task.ext.prefix}_decoupler_plot.png + touch deseq2_estimate_decoupler.tsv + touch deseq2_pvals_decoupler.tsv + touch deseq2_estimate_decoupler_plot.png touch versions.yml """ } diff --git a/modules/nf-core/decoupler/decoupler/meta.yml b/modules/nf-core/decoupler/decoupler/meta.yml index b6866508dfb5..fb20282e1872 100644 --- a/modules/nf-core/decoupler/decoupler/meta.yml +++ b/modules/nf-core/decoupler/decoupler/meta.yml @@ -41,21 +41,31 @@ input: pattern: "*.tsv" ontologies: - edam: http://edamontology.org/format_3475 # TSV - - net: - type: file - description: | - The prior knowledge network linking the features of the - expression matrix to a process/component (e.g. gene set, - transcription factor, kinase, etc.) - pattern: "*.tsv" - ontologies: - - edam: http://edamontology.org/format_3475 # TSV - - gtf: - type: file - description: | - Annotation file in GTF format - pattern: "*.gtf" - ontologies: [] + - - meta2: + type: map + description: Groovy map + - net: + type: file + description: | + The prior knowledge network linking the features of the + expression matrix to a process/component (e.g. gene set, + transcription factor, kinase, etc.) + pattern: "*.tsv" + ontologies: + - edam: http://edamontology.org/format_3475 # TSV + - - meta3: + type: map + description: Groovy map + - annot: + type: file + description: | + Annotation file in TSV format containing gene ID to gene name mappings. + Used specifically for mapping Ensembl IDs to gene names. + Expected format: two columns with gene_id and gene_name headers. + pattern: "*.tsv" + required: false + ontologies: + - edam: http://edamontology.org/format_3475 # TSV output: dc_estimate: - - meta: @@ -90,12 +100,12 @@ output: description: | Groovy Map containing sample information e.g. [ id:‘test’, single_end ] - - "*decoupler_plot.png": + - "*estimate_decoupler_plot.png": type: file description: | The file containing the plots associated to the estimation results of the enrichment(s) - pattern: "*decoupler_plot.png" + pattern: "*estimate_decoupler_plot.png" ontologies: - edam: http://edamontology.org/format_3475 # TSV versions: diff --git a/modules/nf-core/decoupler/decoupler/templates/decoupler.py b/modules/nf-core/decoupler/decoupler/templates/decoupler.py index b0869c4bf0dd..f7ca4a0f5205 100644 --- a/modules/nf-core/decoupler/decoupler/templates/decoupler.py +++ b/modules/nf-core/decoupler/decoupler/templates/decoupler.py @@ -41,38 +41,10 @@ def parse_ext_args(args_string: str): parser.add_argument("--column", type=str, default="log2FoldChange", help="Column name to use for transposition") parser.add_argument("--ensembl_ids", type=str, default="FALSE", help="Convert ENSEMBL IDs to gene symbols if TRUE") parser.add_argument("--methods", type=str, default = "ulm", help="Comma-separated list of methods to use (e.g., 'mlm,ulm')") + parser.add_argument("--features_id_col", type=str, default="gene_id", help="Column name for feature IDs") + parser.add_argument("--features_symbol_col", type=str, default="gene_name", help="Column name for feature symbols") return parser.parse_args(args_list) -def parse_gtf(gtf_file: str): - """ - Parse an optional GTF file to create a mapping of ENSEMBL gene IDs to gene symbols (required to use Progeny data). - """ - mapping = {} - opener = gzip.open if gtf_file.endswith('.gz') else open - with opener(gtf_file, 'rt') as f: - for line in f: - if line.startswith("#"): - continue - fields = line.strip().split("\t") - if len(fields) < 9: - continue - attributes_field = fields[8] - attributes = {} - for attr in attributes_field.split(";"): - attr = attr.strip() - if not attr: - continue - parts = attr.split(" ", 1) - if len(parts) != 2: - continue - key, value = parts - attributes[key] = value.replace('"', '').strip() - gene_id = attributes.get("gene_id") - gene_symbol = attributes.get("gene_name") or attributes.get("gene_symbol") or attributes.get("external_gene_name") - if gene_id and gene_symbol: - mapping[gene_id] = gene_symbol - return mapping - # Parse external arguments raw_args = "${task.ext.args}" parsed_args = parse_ext_args(raw_args) @@ -80,13 +52,24 @@ def parse_gtf(gtf_file: str): if parsed_args.ensembl_ids.upper() == "TRUE": try: - gene_mapping = parse_gtf("${gtf}") + if not os.path.exists("${annot}"): + raise FileNotFoundError(f"Annotation file not found: ${annot}") + + annot_df = pd.read_csv("${annot}", sep="\t") + + required_cols = {parsed_args.features_id_col, parsed_args.features_symbol_col} + + missing = required_cols - set(annot_df.columns) + if missing: + raise ValueError(f"Missing required columns in annotation file: {missing}. Available columns: {list(annot_df.columns)}") + + gene_mapping = dict(zip(annot_df[parsed_args.features_id_col], annot_df[parsed_args.features_symbol_col])) new_index = [gene_mapping.get(ens, None) for ens in mat.index] mat.index = new_index mat = mat[mat.index.notnull()] mat = mat[~mat.index.duplicated(keep='first')] except Exception as e: - print("ERROR: Failed to parse GTF:", e) + print(f"ERROR: Failed to process annotation file: {e}") sys.exit(1) if parsed_args.transpose.upper() == "TRUE": diff --git a/modules/nf-core/decoupler/decoupler/tests/main.nf.test b/modules/nf-core/decoupler/decoupler/tests/main.nf.test index 932807e1d1a9..9e1b826e541e 100644 --- a/modules/nf-core/decoupler/decoupler/tests/main.nf.test +++ b/modules/nf-core/decoupler/decoupler/tests/main.nf.test @@ -14,7 +14,7 @@ nextflow_process { when { params { - module_args = "--min_n 2 --transpose TRUE --ensembl_ids TRUE --methods mlm" + module_args = "--min_n 2 --transpose TRUE --ensembl_ids TRUE --methods mlm --features_id_col gene_id --features_symbol_col gene_name" } process { """ @@ -23,9 +23,12 @@ nextflow_process { file("https://github.com/nf-core/test-datasets/raw/differentialabundance/modules_testdata/treatment_mCherry_hND6_.deseq2.results.tsv", checkIfExists: true) ] input[1] = [ + ['id':'network'], file("https://github.com/nf-core/test-datasets/raw/differentialabundance/modules_testdata/progeny/mouse_network.tsv", checkIfExists: true) ] - input[2] = [file("https://ftp.ensembl.org/pub/release-81/gtf/mus_musculus/Mus_musculus.GRCm38.81.gtf.gz", checkIfExists: true)] + input[2] = [ + ['id':'annot'], + file("https://github.com/nf-core/test-datasets/raw/differentialabundance/modules_testdata/Mus_musculus.anno.feature_metadata.tsv", checkIfExists: true)] """ } } @@ -63,7 +66,7 @@ nextflow_process { when { params { - module_args = "--min_n 1" + module_args = "--min_n 1 --features_id_col gene_id --features_symbol_col gene_name" } process { @@ -73,9 +76,12 @@ nextflow_process { file(params.test_data['generic']['tsv']['expression'], checkIfExists: true) ] input[1] = [ + ['id':'network'], file(params.test_data['generic']['tsv']['network'], checkIfExists: true) ] - input[2] = [file("https://ftp.ensembl.org/pub/release-81/gtf/mus_musculus/Mus_musculus.GRCm38.81.gtf.gz", checkIfExists: true)] + input[2] = [ + ['id':'annot'], + file("https://github.com/nf-core/test-datasets/raw/differentialabundance/modules_testdata/Mus_musculus.anno.feature_metadata.tsv", checkIfExists: true)] """ } } @@ -114,7 +120,7 @@ nextflow_process { when { params { - module_args = "--min_n 1" + module_args = "--min_n 1 --features_id_col gene_id --features_symbol_col gene_name" } process { """ @@ -123,9 +129,12 @@ nextflow_process { file(params.test_data['generic']['tsv']['expression'], checkIfExists: true) ] input[1] = [ + ['id':'network'], file(params.test_data['generic']['tsv']['network'], checkIfExists: true) ] - input[2] = [file("https://ftp.ensembl.org/pub/release-81/gtf/mus_musculus/Mus_musculus.GRCm38.81.gtf.gz", checkIfExists: true)] + input[2] = [ + ['id':'annot'], + file("https://github.com/nf-core/test-datasets/raw/differentialabundance/modules_testdata/Mus_musculus.anno.feature_metadata.tsv", checkIfExists: true)] """ } } diff --git a/modules/nf-core/decoupler/decoupler/tests/main.nf.test.snap b/modules/nf-core/decoupler/decoupler/tests/main.nf.test.snap index f7133a7585ec..cdc1a1999472 100644 --- a/modules/nf-core/decoupler/decoupler/tests/main.nf.test.snap +++ b/modules/nf-core/decoupler/decoupler/tests/main.nf.test.snap @@ -3,7 +3,7 @@ "content": [ "[deseq2_treatment_mCherry_hND6_consensus_estimate_decoupler.tsv, deseq2_treatment_mCherry_hND6_mlm_estimate_decoupler.tsv]", "[deseq2_treatment_mCherry_hND6_consensus_pvals_decoupler.tsv, deseq2_treatment_mCherry_hND6_mlm_pvals_decoupler.tsv]", - "[deseq2_treatment_mCherry_hND6_consensus_estimate_decoupler_plot.png, deseq2_treatment_mCherry_hND6_consensus_pvals_decoupler_plot.png, deseq2_treatment_mCherry_hND6_mlm_estimate_decoupler_plot.png, deseq2_treatment_mCherry_hND6_mlm_pvals_decoupler_plot.png]", + "[deseq2_treatment_mCherry_hND6_consensus_estimate_decoupler_plot.png, deseq2_treatment_mCherry_hND6_mlm_estimate_decoupler_plot.png]", 2, 2, [ @@ -26,7 +26,7 @@ "nf-test": "0.9.2", "nextflow": "25.04.6" }, - "timestamp": "2025-07-17T03:01:11.305706897" + "timestamp": "2025-08-12T18:24:03.118968335" }, "decoupler stub": { "content": [ @@ -36,7 +36,7 @@ { "id": "test" }, - "null_test_estimate_decoupler.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "deseq2_estimate_decoupler.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "1": [ @@ -44,7 +44,7 @@ { "id": "test" }, - "null_test_pvals_decoupler.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "deseq2_pvals_decoupler.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "2": [ @@ -52,7 +52,7 @@ { "id": "test" }, - "null_test_decoupler_plot.png:md5,d41d8cd98f00b204e9800998ecf8427e" + "deseq2_estimate_decoupler_plot.png:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "3": [ @@ -63,7 +63,7 @@ { "id": "test" }, - "null_test_estimate_decoupler.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "deseq2_estimate_decoupler.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "dc_pvals": [ @@ -71,7 +71,7 @@ { "id": "test" }, - "null_test_pvals_decoupler.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "deseq2_pvals_decoupler.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "png": [ @@ -79,7 +79,7 @@ { "id": "test" }, - "null_test_decoupler_plot.png:md5,d41d8cd98f00b204e9800998ecf8427e" + "deseq2_estimate_decoupler_plot.png:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "versions": [ @@ -91,13 +91,13 @@ "nf-test": "0.9.2", "nextflow": "25.04.6" }, - "timestamp": "2025-07-17T01:49:26.123404951" + "timestamp": "2025-08-25T18:51:20.453145611" }, "decoupler_test": { "content": [ "[deseq2_test_consensus_estimate_decoupler.tsv, deseq2_test_ulm_estimate_decoupler.tsv]", "[deseq2_test_consensus_pvals_decoupler.tsv, deseq2_test_ulm_pvals_decoupler.tsv]", - "[deseq2_test_consensus_estimate_decoupler_plot.png, deseq2_test_consensus_pvals_decoupler_plot.png, deseq2_test_ulm_estimate_decoupler_plot.png, deseq2_test_ulm_pvals_decoupler_plot.png]", + "[deseq2_test_consensus_estimate_decoupler_plot.png, deseq2_test_ulm_estimate_decoupler_plot.png]", 2, 2, [ @@ -120,6 +120,6 @@ "nf-test": "0.9.2", "nextflow": "25.04.6" }, - "timestamp": "2025-07-17T04:07:01.430446461" + "timestamp": "2025-08-12T18:27:18.094901759" } } \ No newline at end of file diff --git a/subworkflows/nf-core/differential_functional_enrichment/main.nf b/subworkflows/nf-core/differential_functional_enrichment/main.nf index f52ec8197ef5..c75b5b58fcc0 100644 --- a/subworkflows/nf-core/differential_functional_enrichment/main.nf +++ b/subworkflows/nf-core/differential_functional_enrichment/main.nf @@ -8,6 +8,7 @@ include { CUSTOM_TABULARTOGSEACLS } from '../../../modules/nf-core/custom/tabul include { CUSTOM_TABULARTOGSEACHIP } from '../../../modules/nf-core/custom/tabulartogseachip/main.nf' include { GSEA_GSEA } from '../../../modules/nf-core/gsea/gsea/main.nf' include { PROPR_GREA } from "../../../modules/nf-core/propr/grea/main.nf" +include { DECOUPLER } from '../../../modules/nf-core/decoupler/decoupler/main' // Combine meta maps, including merging non-identical values of shared keys (e.g. 'id') def mergeMaps(meta, meta2){ @@ -37,10 +38,9 @@ workflow DIFFERENTIAL_FUNCTIONAL_ENRICHMENT { // Add method information into meta map of ch_input // This information is used later to determine which method to run for each input // Also, reorganize the structure to match them with the modules' input organization - ch_input_for_other = ch_input - .multiMap { - meta, file, genesets, background, method -> + .join(ch_featuresheet) + .multiMap { meta, file, genesets, background, method, features_sheet, features_id, features_symbol -> def meta_with_method = meta + [ 'functional_method': method ] input: [ meta_with_method, file ] @@ -48,8 +48,10 @@ workflow DIFFERENTIAL_FUNCTIONAL_ENRICHMENT { [ meta_with_method, genesets ] background: [ meta_with_method, background ] + features: + [ meta_with_method, features_sheet, features_id, features_symbol] } - + // In the case of GSEA, it needs additional files coming from other channels that other methods don't use // here we define the input channel for the GSEA section @@ -80,6 +82,7 @@ workflow DIFFERENTIAL_FUNCTIONAL_ENRICHMENT { .combine(ch_contrasts_transposed, by:0) .multiMap(criteria) + // ---------------------------------------------------- // Perform enrichment analysis with gprofiler2 // ---------------------------------------------------- @@ -116,6 +119,16 @@ workflow DIFFERENTIAL_FUNCTIONAL_ENRICHMENT { ch_input_for_gsea.map{ tuple(it[0].reference, it[0].target) }, CUSTOM_TABULARTOGSEACHIP.out.chip.first() ) + // ---------------------------------------------------- + // Perform enrichment analysis with DECOUPLER + // ---------------------------------------------------- + + DECOUPLER( + ch_input_for_other.input.filter{ it[0].functional_method == 'decoupler' }, + ch_input_for_other.genesets.filter{ it[0].functional_method == 'decoupler'}, + ch_input_for_other.features.filter{ it[0].functional_method == 'decoupler'} + .map{ meta, features_sheet, features_id, features_symbol -> [meta, features_sheet] } + ) // ---------------------------------------------------- // Perform enrichment analysis with GREA @@ -133,6 +146,7 @@ workflow DIFFERENTIAL_FUNCTIONAL_ENRICHMENT { .mix(CUSTOM_TABULARTOGSEACLS.out.versions) .mix(CUSTOM_TABULARTOGSEACHIP.out.versions) .mix(GSEA_GSEA.out.versions) + .mix(DECOUPLER.out.versions) .mix(PROPR_GREA.out.versions) emit: @@ -147,6 +161,11 @@ workflow DIFFERENTIAL_FUNCTIONAL_ENRICHMENT { // gsea-specific outputs gsea_report = GSEA_GSEA.out.report_tsvs_ref.join(GSEA_GSEA.out.report_tsvs_target) + // decoupler-specific outputs + decoupler_dc_estimate = DECOUPLER.out.dc_estimate + decoupler_dc_pvals = DECOUPLER.out.dc_pvals + decoupler_png = DECOUPLER.out.png + // grea-specific outputs grea_results = PROPR_GREA.out.results diff --git a/subworkflows/nf-core/differential_functional_enrichment/meta.yml b/subworkflows/nf-core/differential_functional_enrichment/meta.yml index dc1f80997018..0882d4b55f03 100644 --- a/subworkflows/nf-core/differential_functional_enrichment/meta.yml +++ b/subworkflows/nf-core/differential_functional_enrichment/meta.yml @@ -8,6 +8,7 @@ keywords: components: - gprofiler2/gost - gsea/gsea + - decoupler - propr/grea - custom/tabulartogseagct - custom/tabulartogseacls @@ -35,7 +36,7 @@ input: For the moment, it is only required for gprofiler2. - analysis_method: type: value - description: Analysis method (gprofiler2, gsea, or grea) + description: Analysis method (gprofiler2, gsea, decoupler or grea) - ch_contrasts: description: Channel with contrast information structure: @@ -60,7 +61,7 @@ input: type: list description: List of contrast formula - comparison: - type list: + type: list description: List of contrast comparison - ch_samplesheet: description: Channel with sample information @@ -104,11 +105,12 @@ output: description: Metadata map - all_enrich: type: file - description: table listing all enriched pathways that were found by gprofiler2. + description: | + Table listing all enriched pathways that were found by gprofiler2. It can be empty, if none is found. pattern: "*.gprofiler2.all_enriched_pathways.tsv" - gprofiler2_sub_enrich: - description: Channel containing the secondary enrichment table output from gprofiler. + description: Channel containing the secondary enrichment table output from gprofiler2. structure: - meta: type: map @@ -116,7 +118,7 @@ output: - sub_enrich: type: file description: | - table listing enriched pathways that were found from one particular source. + Table listing enriched pathways that were found from one particular source. Note that it will only be created if any were found. pattern: "*.gprofiler2.*.sub_enriched_pathways.tsv" - gprofiler2_plot_html: @@ -128,7 +130,7 @@ output: - plot_html: type: file description: | - Channel containing HTML file; interactive Manhattan plot of all enriched pathways. + HTML file; interactive Manhattan plot of all enriched pathways. Note that this file will only be generated if enriched pathways were found. pattern: "*.gprofiler2.gostplot.html" - gsea_report: @@ -145,9 +147,49 @@ output: type: file description: Main TSV results report file for the target group. pattern: "*gsea_report_for_${target}.tsv" + - decoupler_dc_estimate: + description: Channel containing decoupler estimate results. + structure: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end ] + - estimate_files: + type: file + description: | + Files containing the estimation results of the enrichment(s) + pattern: "*estimate_decoupler.tsv" + - decoupler_dc_pvals: + description: Channel containing decoupler pvals results. + structure: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end ] + - pvals_files: + type: file + description: | + Files containing the p-values associated to the estimation + results of the enrichment(s) + pattern: "*pvals_decoupler.tsv" + - decoupler_png: + description: Channel containing decoupler png results. + structure: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end ] + - plot_files: + type: file + description: | + Files containing the plots associated to the estimation + results of the enrichment(s) + pattern: "*estimate_decoupler_plot.png" - grea_results: - description: | - Channel containing the output from GREA. + description: Channel containing the output from GREA. structure: - meta: type: map diff --git a/subworkflows/nf-core/differential_functional_enrichment/tests/all.config b/subworkflows/nf-core/differential_functional_enrichment/tests/all.config index eb32d8ca4bba..57ae06008703 100644 --- a/subworkflows/nf-core/differential_functional_enrichment/tests/all.config +++ b/subworkflows/nf-core/differential_functional_enrichment/tests/all.config @@ -61,4 +61,9 @@ process { withName: "PROPR_GREA"{ ext.args = { "--permutation 10 --set_min 10 --seed 123 --round_digits 5"} } + + withName: 'DECOUPLER' { + ext.prefix = { "${meta.method_differential}_${meta.id}" } + ext.args = "--min_n 2 --transpose TRUE --ensembl_ids TRUE --methods mlm" + } } diff --git a/subworkflows/nf-core/differential_functional_enrichment/tests/decoupler.config b/subworkflows/nf-core/differential_functional_enrichment/tests/decoupler.config new file mode 100644 index 000000000000..763d65ae6deb --- /dev/null +++ b/subworkflows/nf-core/differential_functional_enrichment/tests/decoupler.config @@ -0,0 +1,22 @@ +params { + decoupler_network = "https://github.com/nf-core/test-datasets/raw/differentialabundance/modules_testdata/progeny/mouse_network.tsv" + method_differential = 'deseq2' +} + +process { + withName: 'DECOUPLER' { + ext.prefix = { + def method = meta.params.differential_method ?: meta.differential_method ?: "unknown" + def prefix = "${method}_${meta.id}" + return prefix + } + ext.args = { + def features_id = meta.params?.features_id_col ?: 'gene_id' + def features_symbol = meta.params?.features_name_col ?: 'gene_name' + def min_n = meta.params?.decoupler_min_n ?: 5 + def methods = meta.params?.decoupler_methods ?: 'mlm' + + "--min_n ${min_n} --transpose TRUE --ensembl_ids TRUE --methods ${methods} --features_id_col ${features_id} --features_symbol_col ${features_symbol}" + } + } +} diff --git a/subworkflows/nf-core/differential_functional_enrichment/tests/main.nf.test b/subworkflows/nf-core/differential_functional_enrichment/tests/main.nf.test index fadbb68a4191..0e69dc9d210d 100644 --- a/subworkflows/nf-core/differential_functional_enrichment/tests/main.nf.test +++ b/subworkflows/nf-core/differential_functional_enrichment/tests/main.nf.test @@ -19,6 +19,8 @@ nextflow_workflow { tag "custom/tabulartogseachip" tag "subworkflows/abundance_differential_filter" tag "abundance_differential_filter" + tag "decoupler_basic" + tag "decoupler/decoupler" test("test gprofiler2 - mouse") { tag 'gprofiler2_basic' @@ -39,7 +41,12 @@ nextflow_workflow { input[0] = ch_input input[1] = Channel.of([[], [], [], [], [], []]) input[2] = Channel.of([[], []]) - input[3] = Channel.of([[], [], [], []]) + input[3] = Channel.of([ + ['id':'Condition_genotype_WT_KO', 'variable':'Condition genotype', 'reference':'WT', 'target':'KO', 'blocking':'batch'], + [], // + '', // + '' // + ]) """ } } @@ -114,10 +121,15 @@ nextflow_workflow { [meta, results, [], [], 'gprofiler2'] } + ch_featuresheet = ch_input + .map { meta, file, genesets, background, method -> + [meta, [], '', ''] + } + input[0] = ch_input input[1] = Channel.of([[], [], [], [], [], []]) input[2] = Channel.of([[], []]) - input[3] = Channel.of([[], [], [], []]) + input[3] = ch_featuresheet """ } } @@ -303,11 +315,14 @@ nextflow_workflow { .map { meta, results, genesets -> [meta, results, genesets, [], 'grea'] } - + ch_featuresheet = ch_input + .map { meta, file, genesets, background, method -> + [meta, [], '', ''] + } input[0] = ch_input input[1] = Channel.of([[], [], [], [], [], []]) input[2] = Channel.of([[], []]) - input[3] = Channel.of([[], [], [], []]) + input[3] = ch_featuresheet """ } } @@ -463,6 +478,73 @@ nextflow_workflow { } + test("deseq2 + decoupler - mouse") { + tag "deseq2+decoupler" + tag "decoupler_basic" + config "./decoupler.config" + + when { + workflow { + """ + ch_input = Channel.of([ + [ + 'id':'treatment_mCherry_hND6', + 'method_differential':'deseq2', + 'params': [ + 'differential_method': 'deseq2' + ] + ], + file("https://github.com/nf-core/test-datasets/raw/differentialabundance/modules_testdata/treatment_mCherry_hND6_.deseq2.results.tsv", checkIfExists: true), + file("https://github.com/nf-core/test-datasets/raw/differentialabundance/modules_testdata/progeny/mouse_network.tsv", checkIfExists: true), + [], + 'decoupler' + ]) + + ch_contrasts = Channel.of([[], [], [], [], [], []]) + ch_samplesheet = Channel.of([[], []]) + ch_featuresheet = Channel.of([ + [ + 'id':'treatment_mCherry_hND6', + 'method_differential':'deseq2', + 'params': [ + 'differential_method': 'deseq2' + ] + ], + file("https://raw.githubusercontent.com/nf-core/test-datasets/refs/heads/differentialabundance/modules_testdata/Mus_musculus.anno.tsv", checkIfExists: true), + 'gene_id', + 'gene_name' + ]) + + input[0] = ch_input + input[1] = ch_contrasts + input[2] = ch_samplesheet + input[3] = ch_featuresheet + """ + } + } + + then { + assertAll( + { assert workflow.success }, + { assert workflow.out.decoupler_dc_estimate.size() > 0 }, + { assert workflow.out.decoupler_dc_pvals.size() > 0 }, + { assert workflow.out.decoupler_png.size() > 0 }, + { assert snapshot( + workflow.out.decoupler_dc_estimate.collect { meta, files -> + [meta.id, files.collect { path(it).getFileName().toString() }.sort()] + }, + workflow.out.decoupler_dc_pvals.collect { meta, files -> + [meta.id, files.collect { path(it).getFileName().toString() }.sort()] + }, + workflow.out.decoupler_png.collect { meta, files -> + [meta.id, files.collect { path(it).getFileName().toString() }.sort()] + }, + workflow.out.versions + ).match() } + ) + } + } + test("stub") { tag 'gprofiler2_basic' config './gprofiler2.config' @@ -482,7 +564,12 @@ nextflow_workflow { input[0] = ch_input input[1] = Channel.of([[], [], [], [], [], []]) input[2] = Channel.of([[], []]) - input[3] = Channel.of([[], [], [], []]) + input[3] = Channel.of([ + ['id':'Condition_genotype_WT_KO', 'variable':'Condition genotype', 'reference':'WT', 'target':'KO', 'blocking':'batch'], + [], + '', + '' + ]) """ } } @@ -495,4 +582,4 @@ nextflow_workflow { } } -} +} \ No newline at end of file diff --git a/subworkflows/nf-core/differential_functional_enrichment/tests/main.nf.test.snap b/subworkflows/nf-core/differential_functional_enrichment/tests/main.nf.test.snap index 4e6fe0334345..6c892fc00f28 100644 --- a/subworkflows/nf-core/differential_functional_enrichment/tests/main.nf.test.snap +++ b/subworkflows/nf-core/differential_functional_enrichment/tests/main.nf.test.snap @@ -155,7 +155,7 @@ "nf-test": "0.9.2", "nextflow": "25.04.6" }, - "timestamp": "2025-07-21T15:04:08.996414207" + "timestamp": "2025-09-12T13:53:45.331513742" }, "deseq2 + gprofiler2 - mouse": { "content": [ @@ -250,7 +250,7 @@ "nf-test": "0.9.2", "nextflow": "25.04.6" }, - "timestamp": "2025-07-21T15:00:06.659273279" + "timestamp": "2025-09-12T13:50:16.438987978" }, "propd + grea - mouse": { "content": [ @@ -351,7 +351,46 @@ "nf-test": "0.9.2", "nextflow": "25.04.6" }, - "timestamp": "2025-07-21T14:58:54.929796085" + "timestamp": "2025-09-12T13:48:59.907490432" + }, + "deseq2 + decoupler - mouse": { + "content": [ + [ + [ + "treatment_mCherry_hND6", + [ + "deseq2_treatment_mCherry_hND6_consensus_estimate_decoupler.tsv", + "deseq2_treatment_mCherry_hND6_mlm_estimate_decoupler.tsv" + ] + ] + ], + [ + [ + "treatment_mCherry_hND6", + [ + "deseq2_treatment_mCherry_hND6_consensus_pvals_decoupler.tsv", + "deseq2_treatment_mCherry_hND6_mlm_pvals_decoupler.tsv" + ] + ] + ], + [ + [ + "treatment_mCherry_hND6", + [ + "deseq2_treatment_mCherry_hND6_consensus_estimate_decoupler_plot.png", + "deseq2_treatment_mCherry_hND6_mlm_estimate_decoupler_plot.png" + ] + ] + ], + [ + "versions.yml:md5,d268550490df4c6a9e58baa7d08b93e6" + ] + ], + "meta": { + "nf-test": "0.9.2", + "nextflow": "25.04.6" + }, + "timestamp": "2025-09-01T01:56:15.175941185" }, "deseq2 + gsea - mouse": { "content": [ @@ -453,7 +492,25 @@ ], "5": [ + + ], + "6": [ + + ], + "7": [ + + ], + "8": [ "versions.yml:md5,71d562114ad0a46a870b75ac423ab82c" + ], + "decoupler_dc_estimate": [ + + ], + "decoupler_dc_pvals": [ + + ], + "decoupler_png": [ + ], "gprofiler2_all_enrich": [ [ @@ -509,6 +566,6 @@ "nf-test": "0.9.2", "nextflow": "25.04.6" }, - "timestamp": "2025-07-21T15:04:23.243032039" + "timestamp": "2025-09-12T13:57:00.483231081" } } \ No newline at end of file