diff --git a/modules/nf-core/sequali/environment.yml b/modules/nf-core/sequali/environment.yml new file mode 100644 index 00000000000..b712a24fdd5 --- /dev/null +++ b/modules/nf-core/sequali/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::sequali=0.12.0 diff --git a/modules/nf-core/sequali/main.nf b/modules/nf-core/sequali/main.nf new file mode 100644 index 00000000000..718c07171c9 --- /dev/null +++ b/modules/nf-core/sequali/main.nf @@ -0,0 +1,58 @@ +process SEQUALI { + tag "$meta.id" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'oras://community.wave.seqera.io/library/sequali:0.12.0--c288fa2befb47d0f': + 'community.wave.seqera.io/library/sequali:0.12.0--07485bec824d914a' }" + + input: + + tuple val(meta), path(reads) + + output: + + tuple val(meta), path("*.html"), emit: html + tuple val(meta), path("*.json"), emit: json + 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 read_1_bam = reads.size() == 1 ? reads : reads[0] + def read_2 = reads.size() == 2 ? reads[1]: "" + + """ + sequali \\ + $args \\ + -t $task.cpus \\ + --html ${prefix}.html \\ + --json ${prefix}.json \\ + $read_1_bam \\ + $read_2 + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + sequali: \$(sequali --version |& sed '1!d ; s/sequali //') + END_VERSIONS + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + touch ${prefix}.html + touch ${prefix}.json + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + sequali: \$(sequali --version |& sed '1!d ; s/sequali //') + END_VERSIONS + """ +} + diff --git a/modules/nf-core/sequali/meta.yml b/modules/nf-core/sequali/meta.yml new file mode 100644 index 00000000000..62dc6e85e00 --- /dev/null +++ b/modules/nf-core/sequali/meta.yml @@ -0,0 +1,60 @@ +--- +name: sequali +description: Sequence quality metrics for FASTQ and uBAM files. +keywords: + - quality_control + - qc + - preprocessing +tools: + - sequali: + description: Fast sequencing quality metrics + homepage: "https://github.com/rhpvorderman/sequali" + documentation: "https://sequali.readthedocs.io/en/latest/" + tool_dev_url: "https://github.com/rhpvorderman/sequali" + doi: "10.5281/zenodo.10854010" + licence: ["AGPL v3-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]` + + - reads: + type: file + description: Input FASTQ(s) or uBAM file. The format is autodetected and compressed formats are supported. + pattern: "*.{fastq,fq,fastq.gz,fq.gz,bam}" +output: + - json: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]` + - "*.json": + type: file + description: JSON output file. + pattern: "*.{json}" + - html: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]` + - "*.html": + type: file + description: HTML output file. + pattern: "*.{html}" + + - versions: + - "versions.yml": + type: file + description: File containing software versions. + pattern: "versions.yml" +authors: + - "@irliampa" + - "@DarkoCucin" +maintainers: + - "@irliampa" + - "@DarkoCucin" diff --git a/modules/nf-core/sequali/tests/main.nf.test b/modules/nf-core/sequali/tests/main.nf.test new file mode 100644 index 00000000000..9876153caf6 --- /dev/null +++ b/modules/nf-core/sequali/tests/main.nf.test @@ -0,0 +1,171 @@ +nextflow_process { + + name "Test Process SEQUALI" + script "../main.nf" + process "SEQUALI" + + tag "modules" + tag "modules_nfcore" + tag "sequali" + + test("sarscov2 - fastq single-end [sequali]") { + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true)]) + """ + } + } + then { + assertAll( + { assert snapshot( + path(process.out.html[0][1]).getFileName().toString(), + path(process.out.json[0][1]).getFileName().toString(), + path(process.out.json[0][1]).json.remove('summary'), + process.out.versions + ).match() } + ) + } + + } + test("sarscov2 - fastq paired-end [sequali]") { + + when { + process { + """ + input[0] = Channel.of([ + [id: 'test', single_end: false], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) ] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + path(process.out.html[0][1]).getFileName().toString(), + path(process.out.json[0][1]).getFileName().toString(), + path(process.out.json[0][1]).json.remove('summary'), + process.out.versions + ).match() } + ) + } + + } + + test("sarscov2 - unaligned bam [sequali]") { + + when { + process { + """ + input[0] = Channel.of([ + [id: 'test', single_end: true], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.unaligned.bam', checkIfExists: true)]) + """ + } + } + + then { + def json_file = path(process.out.json[0][1]).json.get('summary') + assertAll( + { assert process.success }, + { assert snapshot( + path(process.out.html[0][1]).getFileName().toString(), + path(process.out.json[0][1]).getFileName().toString(), + path(process.out.json[0][1]).json.remove('summary'), + process.out.versions + ).match() } + ) + } + + } + + test("sarscov2 - fastq single-end - stub[sequali]") { + + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true)]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + path(process.out.html[0][1]).getFileName().toString(), + path(process.out.json[0][1]).getFileName().toString(), + process.out + ).match() } + ) + } + + } + test("sarscov2 - fastq paired-end - stub[sequali]") { + + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [id: 'test', single_end: false], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) ] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + path(process.out.html[0][1]).getFileName().toString(), + path(process.out.json[0][1]).getFileName().toString(), + process.out + ).match() } + ) + } + + } + + test("sarscov2 - unaligned bam - stub[sequali]") { + + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [id: 'test', single_end: true], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.unaligned.bam', checkIfExists: true)]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + path(process.out.html[0][1]).getFileName().toString(), + path(process.out.json[0][1]).getFileName().toString(), + process.out + ).match() } + ) + } + + } + +} diff --git a/modules/nf-core/sequali/tests/main.nf.test.snap b/modules/nf-core/sequali/tests/main.nf.test.snap new file mode 100644 index 00000000000..441c352bea4 --- /dev/null +++ b/modules/nf-core/sequali/tests/main.nf.test.snap @@ -0,0 +1,245 @@ +{ + "sarscov2 - unaligned bam - stub[sequali]": { + "content": [ + "test.html", + "test.json", + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": true + }, + "test.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + "versions.yml:md5,afd9522ef20d5fe27e0b6aab541c7c30" + ], + "html": [ + [ + { + "id": "test", + "single_end": true + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "json": [ + [ + { + "id": "test", + "single_end": true + }, + "test.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,afd9522ef20d5fe27e0b6aab541c7c30" + ] + } + ], + "meta": { + "nf-test": "0.9.1", + "nextflow": "24.10.0" + }, + "timestamp": "2024-10-31T14:30:30.653577" + }, + "sarscov2 - fastq paired-end [sequali]": { + "content": [ + "test.html", + "test.json", + { + "mean_length": 138.97, + "minimum_length": 72, + "maximum_length": 151, + "total_reads": 100, + "q20_reads": 91, + "total_bases": 13897, + "q20_bases": 12922, + "total_gc_bases": 5430, + "total_n_bases": 0, + "read_pair_info": "Read 1" + }, + [ + "versions.yml:md5,afd9522ef20d5fe27e0b6aab541c7c30" + ] + ], + "meta": { + "nf-test": "0.9.1", + "nextflow": "24.10.0" + }, + "timestamp": "2024-10-31T14:29:34.748364" + }, + "sarscov2 - fastq single-end - stub[sequali]": { + "content": [ + "test.html", + "test.json", + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": true + }, + "test.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + "versions.yml:md5,afd9522ef20d5fe27e0b6aab541c7c30" + ], + "html": [ + [ + { + "id": "test", + "single_end": true + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "json": [ + [ + { + "id": "test", + "single_end": true + }, + "test.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,afd9522ef20d5fe27e0b6aab541c7c30" + ] + } + ], + "meta": { + "nf-test": "0.9.1", + "nextflow": "24.10.0" + }, + "timestamp": "2024-10-31T14:30:06.387152" + }, + "sarscov2 - fastq single-end [sequali]": { + "content": [ + "test.html", + "test.json", + { + "mean_length": 138.97, + "minimum_length": 72, + "maximum_length": 151, + "total_reads": 100, + "q20_reads": 91, + "total_bases": 13897, + "q20_bases": 12922, + "total_gc_bases": 5430, + "total_n_bases": 0, + "read_pair_info": null + }, + [ + "versions.yml:md5,afd9522ef20d5fe27e0b6aab541c7c30" + ] + ], + "meta": { + "nf-test": "0.9.1", + "nextflow": "24.10.0" + }, + "timestamp": "2024-10-31T14:28:42.093895" + }, + "sarscov2 - unaligned bam [sequali]": { + "content": [ + "test.html", + "test.json", + { + "mean_length": 138.97, + "minimum_length": 72, + "maximum_length": 151, + "total_reads": 100, + "q20_reads": 91, + "total_bases": 13897, + "q20_bases": 12922, + "total_gc_bases": 5430, + "total_n_bases": 0, + "read_pair_info": null + }, + [ + "versions.yml:md5,afd9522ef20d5fe27e0b6aab541c7c30" + ] + ], + "meta": { + "nf-test": "0.9.1", + "nextflow": "24.10.0" + }, + "timestamp": "2024-10-31T14:29:54.657233" + }, + "sarscov2 - fastq paired-end - stub[sequali]": { + "content": [ + "test.html", + "test.json", + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": false + }, + "test.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + "versions.yml:md5,afd9522ef20d5fe27e0b6aab541c7c30" + ], + "html": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "json": [ + [ + { + "id": "test", + "single_end": false + }, + "test.json:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,afd9522ef20d5fe27e0b6aab541c7c30" + ] + } + ], + "meta": { + "nf-test": "0.9.1", + "nextflow": "24.10.0" + }, + "timestamp": "2024-10-31T14:30:18.654267" + } +} \ No newline at end of file