diff --git a/modules/nf-core/vembrane/sort/environment.yml b/modules/nf-core/vembrane/sort/environment.yml new file mode 100644 index 00000000000..825a5fac4b7 --- /dev/null +++ b/modules/nf-core/vembrane/sort/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::vembrane=2.4.0 diff --git a/modules/nf-core/vembrane/sort/main.nf b/modules/nf-core/vembrane/sort/main.nf new file mode 100644 index 00000000000..fe6d68a47dc --- /dev/null +++ b/modules/nf-core/vembrane/sort/main.nf @@ -0,0 +1,58 @@ +process VEMBRANE_SORT { + tag "${meta.id}" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container + ? 'https://depot.galaxyproject.org/singularity/vembrane:2.4.0--pyhdfd78af_0' + : 'biocontainers/vembrane:2.4.0--pyhdfd78af_0'}" + + input: + tuple val(meta), path(vcf) + val expression + + output: + tuple val(meta), path("*.vcf*"), emit: vcf + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def suffix = args.contains('--output-fmt vcf.gz') + ? 'vcf.gz' + : args.contains('--output-fmt bcf') + ? 'bcf' + : args.contains('--output-fmt bcf.gz') ? 'bcf.gz' : 'vcf' + """ + vembrane sort \\ + ${args} \\ + --output ${prefix}.${suffix} \\ + '${expression}' \\ + ${vcf} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + vembrane: \$(vembrane --version 2>&1 | head -n1 | sed 's/vembrane //') + END_VERSIONS + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def suffix = args.contains('--output-fmt vcf.gz') + ? 'vcf.gz' + : args.contains('--output-fmt bcf') + ? 'bcf' + : args.contains('--output-fmt bcf.gz') ? 'bcf.gz' : 'vcf' + """ + touch ${prefix}.${suffix} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + vembrane: \$(vembrane --version 2>&1 | head -n1 | sed 's/vembrane //') + END_VERSIONS + """ +} diff --git a/modules/nf-core/vembrane/sort/meta.yml b/modules/nf-core/vembrane/sort/meta.yml new file mode 100644 index 00000000000..8b5c42c0d44 --- /dev/null +++ b/modules/nf-core/vembrane/sort/meta.yml @@ -0,0 +1,63 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json +name: "vembrane_sort" +description: Sort VCF/BCF files by custom Python expressions for variant prioritization +keywords: + - vcf + - bcf + - sort + - genomics + - variant + - prioritization +tools: + - "vembrane": + description: "Filter VCF/BCF files with Python expressions" + homepage: "https://vembrane.github.io/" + documentation: "https://github.com/vembrane/vembrane/blob/main/docs/sort.md" + tool_dev_url: "https://github.com/vembrane/vembrane" + doi: "10.1093/bioinformatics/btac810" + licence: ["MIT"] + identifier: biotools:vembrane + args_id: "$args" + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]` + - vcf: + type: file + description: VCF/BCF file to sort + pattern: "*.{vcf,vcf.gz,bcf,bcf.gz}" + ontologies: + - edam: http://edamontology.org/format_3016 # VCF + - expression: + type: string + description: Python expression (or tuple of expressions) returning orderable values to sort by + +output: + vcf: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]` + - "*.vcf*": + type: file + description: Sorted VCF file (can be compressed) + pattern: "*.{vcf,vcf.gz}" + ontologies: + - edam: http://edamontology.org/format_3016 # VCF + versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + ontologies: + - edam: http://edamontology.org/format_3750 # YAML +authors: +- "@mkatsanto" +- "@trangdo-hsc" +maintainers: +- "@mkatsanto" +- "@trangdo-hsc" diff --git a/modules/nf-core/vembrane/sort/tests/main.nf.test b/modules/nf-core/vembrane/sort/tests/main.nf.test new file mode 100644 index 00000000000..2113b848b8e --- /dev/null +++ b/modules/nf-core/vembrane/sort/tests/main.nf.test @@ -0,0 +1,181 @@ +nextflow_process { + + name "Test Process VEMBRANE_SORT" + script "../main.nf" + process "VEMBRANE_SORT" + + tag "modules" + tag "modules_nfcore" + tag "vembrane" + tag "vembrane/sort" + + test("homo_sapiens - [vcf] - vcf - quality_sort") { + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/test.rnaseq.vcf', checkIfExists: true) + ] + input[1] = 'QUAL' + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("homo_sapiens - [vcf] - vcf - position_sort") { + + config "./nextflow.config" + + when { + params { + vembrane_args = '--output-fmt vcf' + } + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/test.rnaseq.vcf', checkIfExists: true) + ] + input[1] = 'CHROM, POS' + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("homo_sapiens - [vcf] - vcf - descending_sort") { + + config "./nextflow.config" + + when { + params { + vembrane_args = '--preserve-annotation-order' + } + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/test.rnaseq.vcf', checkIfExists: true) + ] + input[1] = 'desc(QUAL)' + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("homo_sapiens - [vcf] - vcf - depth_sort") { + + when { + process { + """ + input[0] = [ + [ id:'test_depth' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/test.rnaseq.vcf', checkIfExists: true) + ] + input[1] = 'INFO["DP"] if "DP" in INFO else 0' + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("homo_sapiens - [vcf] - vcf - numeric_expression") { + + when { + process { + """ + input[0] = [ + [ id:'test_numeric' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/test.rnaseq.vcf', checkIfExists: true) + ] + input[1] = 'POS % 1000' + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("homo_sapiens - [vcf] - vcf - multi_field_sort") { + + when { + process { + """ + input[0] = [ + [ id:'test_multi' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/test.rnaseq.vcf', checkIfExists: true) + ] + input[1] = '(CHROM, POS, QUAL)' + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("homo_sapiens - [vcf] - vcf - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ + [ id:'test_stub' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/test.rnaseq.vcf', checkIfExists: true) + ] + input[1] = 'QUAL' + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out, + path(process.out.versions[0]).yaml + ).match() } + ) + } + } + +} diff --git a/modules/nf-core/vembrane/sort/tests/main.nf.test.snap b/modules/nf-core/vembrane/sort/tests/main.nf.test.snap new file mode 100644 index 00000000000..fc21a3171de --- /dev/null +++ b/modules/nf-core/vembrane/sort/tests/main.nf.test.snap @@ -0,0 +1,238 @@ +{ + "homo_sapiens - [vcf] - vcf - numeric_expression": { + "content": [ + { + "0": [ + [ + { + "id": "test_numeric" + }, + "test_numeric.vcf:md5,4ca96524fc3f76d7bf893f4cb4a657f0" + ] + ], + "1": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ], + "vcf": [ + [ + { + "id": "test_numeric" + }, + "test_numeric.vcf:md5,4ca96524fc3f76d7bf893f4cb4a657f0" + ] + ], + "versions": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-10-29T16:25:43.059651" + }, + "homo_sapiens - [vcf] - vcf - multi_field_sort": { + "content": [ + { + "0": [ + [ + { + "id": "test_multi" + }, + "test_multi.vcf:md5,bc3e7d2374c38dbdefc03788f0aa6dfa" + ] + ], + "1": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ], + "vcf": [ + [ + { + "id": "test_multi" + }, + "test_multi.vcf:md5,bc3e7d2374c38dbdefc03788f0aa6dfa" + ] + ], + "versions": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-10-29T16:25:57.553286" + }, + "homo_sapiens - [vcf] - vcf - descending_sort": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.vcf:md5,c843f4078a1e55b518f7bb06f2b7ca95" + ] + ], + "1": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ], + "vcf": [ + [ + { + "id": "test" + }, + "test.vcf:md5,c843f4078a1e55b518f7bb06f2b7ca95" + ] + ], + "versions": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-10-29T16:24:51.085182" + }, + "homo_sapiens - [vcf] - vcf - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test_stub" + }, + "test_stub.vcf:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ], + "vcf": [ + [ + { + "id": "test_stub" + }, + "test_stub.vcf:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ] + }, + { + "VEMBRANE_SORT": { + "vembrane": "2.4.0" + } + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-10-29T17:07:09.663691" + }, + "homo_sapiens - [vcf] - vcf - depth_sort": { + "content": [ + { + "0": [ + [ + { + "id": "test_depth" + }, + "test_depth.vcf:md5,3b1f4f8d274bccc6f9df5029be4da0d7" + ] + ], + "1": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ], + "vcf": [ + [ + { + "id": "test_depth" + }, + "test_depth.vcf:md5,3b1f4f8d274bccc6f9df5029be4da0d7" + ] + ], + "versions": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-10-29T16:25:29.224789" + }, + "homo_sapiens - [vcf] - vcf - position_sort": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.vcf:md5,7baef79f7f0d1927f47b80d49aa18004" + ] + ], + "1": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ], + "vcf": [ + [ + { + "id": "test" + }, + "test.vcf:md5,7baef79f7f0d1927f47b80d49aa18004" + ] + ], + "versions": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-10-29T16:24:35.504169" + }, + "homo_sapiens - [vcf] - vcf - quality_sort": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.vcf:md5,67de3aeba1654914c674bfd8be189577" + ] + ], + "1": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ], + "vcf": [ + [ + { + "id": "test" + }, + "test.vcf:md5,67de3aeba1654914c674bfd8be189577" + ] + ], + "versions": [ + "versions.yml:md5,3e1b564ae9797cf30cb8871cda395a6b" + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-10-29T16:24:19.895047" + } +} \ No newline at end of file diff --git a/modules/nf-core/vembrane/sort/tests/nextflow.config b/modules/nf-core/vembrane/sort/tests/nextflow.config new file mode 100644 index 00000000000..11c78c5322b --- /dev/null +++ b/modules/nf-core/vembrane/sort/tests/nextflow.config @@ -0,0 +1,5 @@ +process { + withName: 'VEMBRANE_SORT' { + ext.args = params.vembrane_args ?: '' + } +}