diff --git a/modules/nf-core/grimer/environment.yml b/modules/nf-core/grimer/environment.yml new file mode 100644 index 000000000000..1ff6d9cabafc --- /dev/null +++ b/modules/nf-core/grimer/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::grimer=1.1.0" diff --git a/modules/nf-core/grimer/main.nf b/modules/nf-core/grimer/main.nf new file mode 100644 index 000000000000..0d10642537b1 --- /dev/null +++ b/modules/nf-core/grimer/main.nf @@ -0,0 +1,41 @@ +process GRIMER { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/grimer:1.1.0--pyhdfd78af_0': + 'quay.io/biocontainers/grimer:1.1.0--pyhdfd78af_0' }" + + input: + tuple val(meta), path(input_table) + path sample_metadata + path config + + output: + tuple val(meta), path("*.html"), emit: report + tuple val("${task.process}"), val('grimer'), eval('grimer --version 2>&1 | tail -1'), emit: versions_grimer, topic: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def m_arg = sample_metadata ? "-m ${sample_metadata}" : '' + def c_arg = config ? "-c ${config}" : '' + """ + grimer \\ + -i ${input_table} \\ + ${m_arg} \\ + ${c_arg} \\ + -o ${prefix}.html \\ + ${args} + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.html + """ +} diff --git a/modules/nf-core/grimer/meta.yml b/modules/nf-core/grimer/meta.yml new file mode 100644 index 000000000000..4aa817b68011 --- /dev/null +++ b/modules/nf-core/grimer/meta.yml @@ -0,0 +1,95 @@ +name: grimer +description: | + Generates an interactive HTML dashboard integrating taxonomy, + annotation, and metadata to detect contamination in metagenomic + and amplicon sequencing datasets. GRIMER is independent of + quantification methods and directly analyses contingency tables. +keywords: + - metagenomics + - contamination + - visualisation + - taxonomy + - viromics + - microbiome + - dashboard +tools: + - grimer: + description: | + GRIMER performs automated contamination analysis and generates + portable interactive dashboards from contingency tables, + integrating taxonomy, annotation and metadata. + homepage: https://github.com/pirovc/grimer + documentation: https://pirovc.github.io/grimer/ + tool_dev_url: https://github.com/pirovc/grimer + doi: "10.1101/2021.06.22.449360" + licence: + - "MIT" + identifier: biotools:grimer + args_id: "$args" +input: + - - meta: + type: map + description: | + Groovy Map containing sample information. + e.g. [ id:'sample1' ] + - input_table: + type: file + description: | + Tab-separated count/observation table or .biom file. + Rows are observations; columns are samples. + pattern: "*.{tsv,biom}" + ontologies: + - edam: http://edamontology.org/format_3475 + - edam: http://edamontology.org/format_3746 + - sample_metadata: + type: file + description: Tab-separated metadata file. Rows are samples; columns are + metadata fields. Optional, pass [] to omit. + pattern: "*.tsv" + ontologies: + - edam: http://edamontology.org/format_3475 + - config: + type: file + description: | + YAML configuration file defining external tool references, + controls, and annotations. Optional, pass [] to omit. + pattern: "*.{yml,yaml}" + ontologies: + - edam: http://edamontology.org/format_3750 +output: + report: + - - meta: + type: map + description: Groovy Map containing sample information. + - "*.html": + type: file + description: | + Interactive HTML dashboard report integrating taxonomy, + contamination analysis, heatmaps, and correlation plots. + pattern: "*.html" + ontologies: [] + versions_grimer: + - - ${task.process}: + type: string + description: The name of the process + - grimer: + type: string + description: The name of the tool + - grimer --version 2>&1 | tail -1: + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - grimer: + type: string + description: The name of the tool + - grimer --version 2>&1 | tail -1: + type: eval + description: The expression to obtain the version of the tool +authors: + - "@Jonahnki" +maintainers: + - "@Jonahnki" diff --git a/modules/nf-core/grimer/tests/main.nf.test b/modules/nf-core/grimer/tests/main.nf.test new file mode 100644 index 000000000000..15faab997837 --- /dev/null +++ b/modules/nf-core/grimer/tests/main.nf.test @@ -0,0 +1,80 @@ +nextflow_process { + + name "Test Process GRIMER" + script "../main.nf" + process "GRIMER" + + tag "modules" + tag "modules_nfcore" + tag "grimer" + + test("grimer - tsv - basic") { + + when { + process { + """ + def count_table = file(workDir.resolve("test_count_table.tsv").toString()) + count_table.text = [ + "#OTU_ID\tsample1\tsample2\tsample3", + "Bacteria;Proteobacteria\t1250\t890\t2100", + "Bacteria;Firmicutes\t430\t1200\t320", + "Viruses;Caudoviricetes\t45\t120\t88" + ].join("\\n") + "\\n" + + input[0] = [ + [ id:"test_sample" ], + count_table + ] + input[1] = [] + input[2] = [] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.report[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions_") } + ).match() } + ) + } + } + + test("grimer - tsv - stub") { + + options "-stub" + + when { + process { + """ + def count_table = file(workDir.resolve("test_count_table.tsv").toString()) + count_table.text = [ + "#OTU_ID\tsample1\tsample2\tsample3", + "Bacteria;Proteobacteria\t1250\t890\t2100", + "Bacteria;Firmicutes\t430\t1200\t320", + "Viruses;Caudoviricetes\t45\t120\t88" + ].join("\\n") + "\\n" + + input[0] = [ + [ id:"test_stub" ], + count_table + ] + input[1] = [] + input[2] = [] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.report[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions_") } + ).match() } + ) + } + } +} diff --git a/modules/nf-core/grimer/tests/main.nf.test.snap b/modules/nf-core/grimer/tests/main.nf.test.snap new file mode 100644 index 000000000000..a867c3aa3dcc --- /dev/null +++ b/modules/nf-core/grimer/tests/main.nf.test.snap @@ -0,0 +1,40 @@ +{ + "grimer - tsv - basic": { + "content": [ + "test_sample.html", + { + "versions_grimer": [ + [ + "GRIMER", + "grimer", + "grimer 1.1.0" + ] + ] + } + ], + "timestamp": "2026-06-04T13:24:01.89404156", + "meta": { + "nf-test": "0.9.5", + "nextflow": "25.04.6" + } + }, + "grimer - tsv - stub": { + "content": [ + "test_stub.html", + { + "versions_grimer": [ + [ + "GRIMER", + "grimer", + "grimer 1.1.0" + ] + ] + } + ], + "timestamp": "2026-06-04T13:25:24.167541988", + "meta": { + "nf-test": "0.9.5", + "nextflow": "25.04.6" + } + } +} \ No newline at end of file