diff --git a/modules/nf-core/parsnp/environment.yml b/modules/nf-core/parsnp/environment.yml new file mode 100644 index 00000000000..cdbf8a5b550 --- /dev/null +++ b/modules/nf-core/parsnp/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::parsnp=2.1.5" diff --git a/modules/nf-core/parsnp/main.nf b/modules/nf-core/parsnp/main.nf new file mode 100644 index 00000000000..dfeecf485b6 --- /dev/null +++ b/modules/nf-core/parsnp/main.nf @@ -0,0 +1,58 @@ +process PARSNP { + tag "${meta.id}" + label 'process_high' + + 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/78/78d7fff752adf0b7ff156545536bbe58f8e6e8c5e1b1513f9a726747ee686900/data' + : 'community.wave.seqera.io/library/parsnp:2.1.5--2c7f64ad14a79523'}" + + input: + tuple val(meta), path(genomes, stageAs: "genomes/*") + path reference + + output: + tuple val(meta), path("*.xmfa"), emit: xmfa + tuple val(meta), path("*.ggr"), emit: ggr + tuple val(meta), path("*.snps.mblocks"), emit: snps_mblocks, optional: true + tuple val(meta), path("*.tree"), emit: tree + tuple val(meta), path("partition"), emit: partition, optional: true + tuple val("${task.process}"), val('parsnp'), eval("parsnp --version 2>/dev/null | tail -n 1 | sed 's/parsnp //'"), topic: versions, emit: versions_parsnp + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + parsnp \\ + -r "${reference}" \\ + -d genomes/ \\ + -o parsnp_out \\ + -p ${task.cpus} \\ + ${args} + + mv parsnp_out/parsnp.xmfa ${prefix}.xmfa + mv parsnp_out/parsnp.ggr ${prefix}.ggr + if [ -f parsnp_out/parsnp.snps.mblocks ]; then + mv parsnp_out/parsnp.snps.mblocks "${prefix}.snps.mblocks" + fi + mv parsnp_out/parsnp.tree ${prefix}.tree + if [ -d parsnp_out/partition ]; then + mv parsnp_out/partition . + fi + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def partition_cmd = args.contains("partition") ? "mkdir partition" : "" + """ + touch ${prefix}.xmfa + touch ${prefix}.ggr + touch ${prefix}.snps.mblocks + touch ${prefix}.tree + ${partition_cmd} + """ +} diff --git a/modules/nf-core/parsnp/meta.yml b/modules/nf-core/parsnp/meta.yml new file mode 100644 index 00000000000..864cfd276a7 --- /dev/null +++ b/modules/nf-core/parsnp/meta.yml @@ -0,0 +1,120 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json +name: "parsnp" +description: "Parsnp is a command-line-tool for efficient microbial core genome alignment and SNP detection." +keywords: + - alignment + - bacteria + - phylogeny + - microbial genomics + - core genome + - SNP + +tools: + - "parsnp": + description: "Parsnp is a command-line-tool for efficient microbial core genome alignment and SNP detection." + homepage: "https://harvest.readthedocs.io/en/latest/content/parsnp/tutorial.html" + documentation: "https://harvest.readthedocs.io/en/latest/content/parsnp/tutorial.html" + tool_dev_url: "https://github.com/marbl/parsnp" + doi: "10.1093/bioinformatics/btae311" + licence: ["custom; see https://raw.githubusercontent.com/marbl/parsnp/master/LICENSE"] + identifier: biotools:parsnp + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - genomes: + type: directory + description: Directory containing genome assemblies in FASTA format to be aligned + - reference: + type: file + description: Reference genome in FASTA format + pattern: "*.{fasta,fa,fna}" + ontologies: + - edam: "http://edamontology.org/format_1929" # FASTA + +output: + xmfa: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*.xmfa": + type: file + description: Core-genome multiple sequence alignment in XMFA format + pattern: "*.xmfa" + ontologies: [] + ggr: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*.ggr": + type: file + description: Compressed binary representation of the alignment generated by the Harvest toolkit, used for visualization with Gingr + pattern: "*.ggr" + ontologies: [] + snps_mblocks: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*.snps.mblocks": + type: file + description: Core-SNP signature of each sequence in FASTA format, used to generate the phylogeny. Absent when no SNPs are detected. + pattern: "*.snps.mblocks" + ontologies: + - edam: "http://edamontology.org/format_1929" # FASTA + tree: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*.tree": + type: file + description: Resulting phylogeny in Newick format + pattern: "*.tree" + ontologies: + - edam: "http://edamontology.org/format_1910" # Newick + partition: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "partition": + type: directory + description: Output directory from partition mode containing results of each partitioned run. Only present when --partition is specified. + pattern: "partition" + versions_parsnp: + - - ${task.process}: + type: string + description: The name of the process + - parsnp: + type: string + description: The name of the tool + - "parsnp --version 2>/dev/null | tail -n 1 | sed 's/parsnp //'": + type: eval + description: The expression to obtain the version of the tool + +topics: + versions: + - - ${task.process}: + type: string + description: The process + - parsnp: + type: string + description: The tool name + - parsnp --version 2>/dev/null | tail -n 1 | sed 's/parsnp //': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@emmcauley" +maintainers: + - "@emmcauley" diff --git a/modules/nf-core/parsnp/tests/main.nf.test b/modules/nf-core/parsnp/tests/main.nf.test new file mode 100644 index 00000000000..c375e927f4a --- /dev/null +++ b/modules/nf-core/parsnp/tests/main.nf.test @@ -0,0 +1,120 @@ +nextflow_process { + + name "Test Process PARSNP" + script "../main.nf" + process "PARSNP" + config "./nextflow.config" + + tag "modules" + tag "modules_nfcore" + tag "parsnp" + + test("candidatus_portiera_aleyrodidarum - fasta") { + when { + params { + module_args = '' + } + process { + """ + def genome = file('https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/prokaryotes/candidatus_portiera_aleyrodidarum/genome/genome.fasta', checkIfExists: true) + def genome2 = genome.copyTo('portiera_2.fa') + + input[0] = [[ id:'test' ], [genome, genome2]] + input[1] = genome + """ + } + } + then { + assertAll( + { assert process.success }, + { assert snapshot(sanitizeOutput(process.out)).match() } + ) + } + } + + test("candidatus_portiera_aleyrodidarum - fasta - stub") { + options "-stub" + + when { + params { + module_args = '' + } + process { + """ + def genome = file('https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/prokaryotes/candidatus_portiera_aleyrodidarum/genome/genome.fasta', checkIfExists: true) + def genome2 = genome.copyTo('portiera_2.fa') + input[0] = [[ id:'test' ], [genome, genome2]] + input[1] = genome + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(sanitizeOutput(process.out)).match() } + ) + } + + } + + test("candidatus_portiera_aleyrodidarum - fasta - partition") { + when { + params { + module_args = '--min-partition-size 1' + } + process { + """ + def genome = file('https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/prokaryotes/candidatus_portiera_aleyrodidarum/genome/genome.fasta', checkIfExists: true) + def genome2 = genome.copyTo('portiera_2.fa') + def genome3 = genome.copyTo('portiera_3.fa') + input[0] = [[ id:'test' ], [genome, genome2, genome3]] + input[1] = genome + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.xmfa, + process.out.ggr, + process.out.snps_mblocks, + process.out.tree, + process.out.versions_parsnp, + file(process.out.partition[0][1]).name + ).match() } + ) + } + + } + + test("candidatus_portiera_aleyrodidarum - fasta - partition - stub") { + options "-stub" + + when { + params { + module_args = '--min-partition-size 1' + } + process { + """ + def genome = file('https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/prokaryotes/candidatus_portiera_aleyrodidarum/genome/genome.fasta', checkIfExists: true) + def genome2 = genome.copyTo('portiera_2.fa') + def genome3 = genome.copyTo('portiera_3.fa') + input[0] = [[ id:'test' ], [genome, genome2, genome3]] + input[1] = genome + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(sanitizeOutput(process.out)).match() } + ) + } + + } + +} diff --git a/modules/nf-core/parsnp/tests/main.nf.test.snap b/modules/nf-core/parsnp/tests/main.nf.test.snap new file mode 100644 index 00000000000..d49047ea320 --- /dev/null +++ b/modules/nf-core/parsnp/tests/main.nf.test.snap @@ -0,0 +1,215 @@ +{ + "candidatus_portiera_aleyrodidarum - fasta - stub": { + "content": [ + { + "ggr": [ + [ + { + "id": "test" + }, + "test.ggr:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "partition": [], + "snps_mblocks": [ + [ + { + "id": "test" + }, + "test.snps.mblocks:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "tree": [ + [ + { + "id": "test" + }, + "test.tree:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_parsnp": [ + [ + "PARSNP", + "parsnp", + "2.1.5" + ] + ], + "xmfa": [ + [ + { + "id": "test" + }, + "test.xmfa:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-05-05T11:25:57.496665" + }, + "candidatus_portiera_aleyrodidarum - fasta": { + "content": [ + { + "ggr": [ + [ + { + "id": "test" + }, + "test.ggr:md5,11bb67383eeed4e2607abf54b38bc332" + ] + ], + "partition": [ + + ], + "snps_mblocks": [ + [ + { + "id": "test" + }, + "test.snps.mblocks:md5,e1f403db7a9fb5ea7fac96fb5410f454" + ] + ], + "tree": [ + [ + { + "id": "test" + }, + "test.tree:md5,e56cd53de993f7437fac3be08a442c0d" + ] + ], + "versions_parsnp": [ + [ + "PARSNP", + "parsnp", + "2.1.5" + ] + ], + "xmfa": [ + [ + { + "id": "test" + }, + "test.xmfa:md5,c6418a3584d1a7e0d265cb007766fa54" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-05-05T11:25:43.191941" + }, + "candidatus_portiera_aleyrodidarum - fasta - partition": { + "content": [ + [ + [ + { + "id": "test" + }, + "test.xmfa:md5,9254c3e2a3b0f42b9959bf989a397b51" + ] + ], + [ + [ + { + "id": "test" + }, + "test.ggr:md5,320ae37a8df8d427041d86fca0c40e4c" + ] + ], + [ + [ + { + "id": "test" + }, + "test.snps.mblocks:md5,b476ad8ee2ce9150a40033b2d419613e" + ] + ], + [ + [ + { + "id": "test" + }, + "test.tree:md5,7d7b3090aa9dec54124f80e202ef5576" + ] + ], + [ + [ + "PARSNP", + "parsnp", + "2.1.5" + ] + ], + "partition" + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-05-06T13:35:34.655406" + }, + "candidatus_portiera_aleyrodidarum - fasta - partition - stub": { + "content": [ + { + "ggr": [ + [ + { + "id": "test" + }, + "test.ggr:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "partition": [ + [ + { + "id": "test" + }, + [ + + ] + ] + ], + "snps_mblocks": [ + [ + { + "id": "test" + }, + "test.snps.mblocks:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "tree": [ + [ + { + "id": "test" + }, + "test.tree:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_parsnp": [ + [ + "PARSNP", + "parsnp", + "2.1.5" + ] + ], + "xmfa": [ + [ + { + "id": "test" + }, + "test.xmfa:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-05-05T11:26:28.884401" + } +} \ No newline at end of file diff --git a/modules/nf-core/parsnp/tests/nextflow.config b/modules/nf-core/parsnp/tests/nextflow.config new file mode 100644 index 00000000000..ebb6a2e7ac8 --- /dev/null +++ b/modules/nf-core/parsnp/tests/nextflow.config @@ -0,0 +1,5 @@ +process { + withName: 'PARSNP' { + ext.args = params.module_args + } +}