From 731d27bba427dd003010de2bf5c099e3dad881b1 Mon Sep 17 00:00:00 2001 From: yuukiiwa Date: Thu, 2 Dec 2021 15:26:35 +0800 Subject: [PATCH 01/43] move samtools outside align processes --- subworkflows/local/align_graphmap2.nf | 12 +----------- subworkflows/local/align_minimap2.nf | 12 +----------- workflows/nanoseq.nf | 22 ++++++++++++++-------- 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/subworkflows/local/align_graphmap2.nf b/subworkflows/local/align_graphmap2.nf index ffbecd5a..4e5f3d2b 100644 --- a/subworkflows/local/align_graphmap2.nf +++ b/subworkflows/local/align_graphmap2.nf @@ -36,17 +36,7 @@ workflow ALIGN_GRAPHMAP2 { GRAPHMAP2_ALIGN ( ch_index ) ch_align_sam = GRAPHMAP2_ALIGN.out.align_sam - /* - * Convert SAM to BAM, sort, index BAM file and run samtools stats, flagstat and idxstats - */ - BAM_SORT_SAMTOOLS ( ch_align_sam ) - ch_sortbam = BAM_SORT_SAMTOOLS.out.sortbam - ch_sortbam_stats_multiqc = BAM_SORT_SAMTOOLS.out.sortbam_stats_multiqc - samtools_version = BAM_SORT_SAMTOOLS.out.versions - emit: graphmap2_version - ch_sortbam - ch_sortbam_stats_multiqc - samtools_version + ch_align_sam } diff --git a/subworkflows/local/align_minimap2.nf b/subworkflows/local/align_minimap2.nf index 50d5b292..48ff2300 100644 --- a/subworkflows/local/align_minimap2.nf +++ b/subworkflows/local/align_minimap2.nf @@ -36,17 +36,7 @@ workflow ALIGN_MINIMAP2 { MINIMAP2_ALIGN ( ch_index ) ch_align_sam = MINIMAP2_ALIGN.out.align_sam - /* - * Convert SAM to BAM, sort, index BAM file and run samtools stats, flagstat and idxstats - */ - BAM_SORT_SAMTOOLS ( ch_align_sam ) - ch_sortbam = BAM_SORT_SAMTOOLS.out.sortbam - ch_sortbam_stats_multiqc = BAM_SORT_SAMTOOLS.out.sortbam_stats_multiqc - samtools_version = BAM_SORT_SAMTOOLS.out.versions - emit: minimap2_version - ch_sortbam - ch_sortbam_stats_multiqc - samtools_version + ch_align_sam } diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index f6c94fc7..c35b07b2 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -136,8 +136,9 @@ def dexseq_options = modules['dexseq'] include { INPUT_CHECK } from '../subworkflows/local/input_check' addParams( options: [:] ) include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome' addParams( genome_options: genome_options ) -include { ALIGN_GRAPHMAP2 } from '../subworkflows/local/align_graphmap2' addParams( index_options: graphmap2_index_options, align_options: graphmap2_align_options, samtools_options: samtools_sort_options ) -include { ALIGN_MINIMAP2 } from '../subworkflows/local/align_minimap2' addParams( index_options: minimap2_index_options, align_options: minimap2_align_options, samtools_options: samtools_sort_options ) +include { ALIGN_GRAPHMAP2 } from '../subworkflows/local/align_graphmap2' addParams( index_options: graphmap2_index_options, align_options: graphmap2_align_options ) +include { ALIGN_MINIMAP2 } from '../subworkflows/local/align_minimap2' addParams( index_options: minimap2_index_options, align_options: minimap2_align_options ) +include { BAM_SORT_SAMTOOLS } from '../subworkflows/local/bam_sort_samtools' addParams( samtools_options: samtools_sort_options ) include { BEDTOOLS_UCSC_BIGWIG } from '../subworkflows/local/bedtools_ucsc_bigwig' addParams( bigwig_options: bigwig_options ) include { BEDTOOLS_UCSC_BIGBED } from '../subworkflows/local/bedtools_ucsc_bigbed' addParams( bigbed_options: bigbed_options ) include { QUANTIFY_STRINGTIE_FEATURECOUNTS } from '../subworkflows/local/quantify_stringtie_featurecounts' addParams( stringtie2_options: stringtie2_options, featurecounts_options: featurecounts_options ) @@ -308,22 +309,27 @@ workflow NANOSEQ{ * SUBWORKFLOW: Align fastq files with minimap2 and sort bam files */ ALIGN_MINIMAP2 ( ch_fasta_index, ch_fastq ) - ch_view_sortbam = ALIGN_MINIMAP2.out.ch_sortbam + ch_align_sam = ALIGN_MINIMAP2.out.ch_align_sam ch_software_versions = ch_software_versions.mix(ALIGN_MINIMAP2.out.minimap2_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ALIGN_MINIMAP2.out.samtools_version.first().ifEmpty(null)) - ch_samtools_multiqc = ALIGN_MINIMAP2.out.ch_sortbam_stats_multiqc.ifEmpty([]) } else { /* * SUBWORKFLOW: Align fastq files with graphmap2 and sort bam files */ ALIGN_GRAPHMAP2 ( ch_fasta_index, ch_fastq ) - ch_view_sortbam = ALIGN_GRAPHMAP2.out.ch_sortbam + ch_align_sam = ALIGN_GRAPHMAP2.out.ch_align_sam ch_software_versions = ch_software_versions.mix(ALIGN_GRAPHMAP2.out.graphmap2_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ALIGN_GRAPHMAP2.out.samtools_version.first().ifEmpty(null)) - ch_samtools_multiqc = ALIGN_GRAPHMAP2.out.ch_sortbam_stats_multiqc.ifEmpty([]) } + //if (DNA structural variant){ + // Your merged samtools sort and index module + //}else{ + BAM_SORT_SAMTOOLS ( ch_sam ) + ch_view_sortbam = BAM_SORT_SAMTOOLS.out.sortbam + ch_software_versions = ch_software_versions.mix(BAM_SORT_SAMTOOLS.out.versions.first().ifEmpty(null)) + ch_samtools_multiqc = ALIGN_GRAPHMAP2.out.sortbam_stats_multiqc.ifEmpty([]) + //} + ch_bedtools_version = Channel.empty() if (!params.skip_bigwig){ From f73b71ea42fdb7853cd51d6ff6226c219be9cbda Mon Sep 17 00:00:00 2001 From: yuukiiwa Date: Thu, 2 Dec 2021 15:30:32 +0800 Subject: [PATCH 02/43] fix linting error --- workflows/nanoseq.nf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index c35b07b2..5e78da24 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -321,14 +321,14 @@ workflow NANOSEQ{ ch_software_versions = ch_software_versions.mix(ALIGN_GRAPHMAP2.out.graphmap2_version.first().ifEmpty(null)) } - //if (DNA structural variant){ - // Your merged samtools sort and index module - //}else{ - BAM_SORT_SAMTOOLS ( ch_sam ) + //if (DNA structural variant){ + // Your merged samtools sort and index module + //}else{ + BAM_SORT_SAMTOOLS ( ch_align_sam ) ch_view_sortbam = BAM_SORT_SAMTOOLS.out.sortbam ch_software_versions = ch_software_versions.mix(BAM_SORT_SAMTOOLS.out.versions.first().ifEmpty(null)) ch_samtools_multiqc = ALIGN_GRAPHMAP2.out.sortbam_stats_multiqc.ifEmpty([]) - //} + //} ch_bedtools_version = Channel.empty() if (!params.skip_bigwig){ From 9da5f8376a5da74d3e7726d9783870cde563c44c Mon Sep 17 00:00:00 2001 From: yuukiiwa Date: Thu, 2 Dec 2021 15:39:17 +0800 Subject: [PATCH 03/43] fix error --- subworkflows/local/align_graphmap2.nf | 1 - subworkflows/local/align_minimap2.nf | 1 - workflows/nanoseq.nf | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/subworkflows/local/align_graphmap2.nf b/subworkflows/local/align_graphmap2.nf index 4e5f3d2b..858cf057 100644 --- a/subworkflows/local/align_graphmap2.nf +++ b/subworkflows/local/align_graphmap2.nf @@ -8,7 +8,6 @@ params.samtools_options = [:] include { GRAPHMAP2_INDEX } from '../../modules/local/graphmap2_index' addParams( options: params.index_options ) include { GRAPHMAP2_ALIGN } from '../../modules/local/graphmap2_align' addParams( options: params.align_options ) -include { BAM_SORT_SAMTOOLS } from './bam_sort_samtools' addParams( options: params.samtools_options ) workflow ALIGN_GRAPHMAP2 { take: diff --git a/subworkflows/local/align_minimap2.nf b/subworkflows/local/align_minimap2.nf index 48ff2300..667a1c35 100644 --- a/subworkflows/local/align_minimap2.nf +++ b/subworkflows/local/align_minimap2.nf @@ -8,7 +8,6 @@ params.samtools_options = [:] include { MINIMAP2_INDEX } from '../../modules/local/minimap2_index' addParams( options: params.index_options ) include { MINIMAP2_ALIGN } from '../../modules/local/minimap2_align' addParams( options: params.align_options ) -include { BAM_SORT_SAMTOOLS } from './bam_sort_samtools' addParams( options: params.samtools_options ) workflow ALIGN_MINIMAP2 { take: diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index 5e78da24..bbf0475f 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -327,7 +327,7 @@ workflow NANOSEQ{ BAM_SORT_SAMTOOLS ( ch_align_sam ) ch_view_sortbam = BAM_SORT_SAMTOOLS.out.sortbam ch_software_versions = ch_software_versions.mix(BAM_SORT_SAMTOOLS.out.versions.first().ifEmpty(null)) - ch_samtools_multiqc = ALIGN_GRAPHMAP2.out.sortbam_stats_multiqc.ifEmpty([]) + ch_samtools_multiqc = BAM_SORT_SAMTOOLS.out.sortbam_stats_multiqc.ifEmpty([]) //} ch_bedtools_version = Channel.empty() From 54c2e06f47f21c32e4ee45a68252485740b031e9 Mon Sep 17 00:00:00 2001 From: yuukiiwa Date: Thu, 2 Dec 2021 15:49:54 +0800 Subject: [PATCH 04/43] fix samtools sort error --- modules/nf-core/modules/samtools/sort/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nf-core/modules/samtools/sort/main.nf b/modules/nf-core/modules/samtools/sort/main.nf index b30f6f45..bfe8d8a1 100644 --- a/modules/nf-core/modules/samtools/sort/main.nf +++ b/modules/nf-core/modules/samtools/sort/main.nf @@ -28,7 +28,7 @@ process SAMTOOLS_SORT { script: def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" """ - samtools sort $options.args -@ $task.cpus -o ${prefix}.bam -T $prefix $bam + samtools sort $options.args -@ $task.cpus -o ${prefix}.sorted.bam -T $prefix $bam cat <<-END_VERSIONS > versions.yml ${getProcessName(task.process)}: ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') From 17b2fcf503fb38e046525b25aa71e0c8e357e04f Mon Sep 17 00:00:00 2001 From: Chen Ying Date: Mon, 6 Dec 2021 21:50:31 +0800 Subject: [PATCH 05/43] update bambu version --- modules/local/bambu.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/bambu.nf b/modules/local/bambu.nf index fab6d607..9549390f 100644 --- a/modules/local/bambu.nf +++ b/modules/local/bambu.nf @@ -10,7 +10,7 @@ process BAMBU { mode: params.publish_dir_mode, saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'') } - conda (params.enable_conda ? "conda-forge::r-base=4.0.3 bioconda::bioconductor-bambu=1.0.2 bioconda::bioconductor-bsgenome=1.58.0" : null) + conda (params.enable_conda ? "conda-forge::r-base=4.0.3 bioconda::bioconductor-bambu=2.0.0 bioconda::bioconductor-bsgenome=1.58.0" : null) container "docker.io/yuukiiwa/nanoseq:bambu_bsgenome" input: From fa43fcf83afef48424c8035132cc375554d543a7 Mon Sep 17 00:00:00 2001 From: Chen Ying Date: Mon, 6 Dec 2021 21:53:48 +0800 Subject: [PATCH 06/43] remove doi link badge --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 609b5506..44666d41 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ [![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/) [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) [![Docker](https://img.shields.io/docker/automated/nfcore/nanoseq.svg)](https://hub.docker.com/r/nfcore/nanoseq) -[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3697959.svg)](https://doi.org/10.5281/zenodo.3697959) [![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23nanoseq-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/nanoseq) [![Follow on Twitter](http://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core) From d5a9659f934c89a3b29baefa6cabf154ea634367 Mon Sep 17 00:00:00 2001 From: Christopher Hakkaart Date: Mon, 13 Dec 2021 10:39:22 +0100 Subject: [PATCH 07/43] Initial commit adding small and structural variant calling --- conf/modules.config | 9 +++ conf/test_full.config | 18 +++--- conf/test_nobc_nodx_vc.config | 26 +++++++++ modules/local/medaka_variant.nf | 49 ++++++++++++++++ modules/local/minimap2_align.nf | 2 + modules/local/samtools_sort_index.nf | 41 +++++++++++++ modules/local/sniffles.nf | 42 ++++++++++++++ nextflow.config | 6 ++ nextflow_schema.json | 23 ++++++++ subworkflows/local/align_graphmap2.nf | 3 +- subworkflows/local/align_minimap2.nf | 1 + subworkflows/local/bam_sort_index_samtools.nf | 37 ++++++++++++ .../local/quantify_stringtie_featurecounts.nf | 2 +- subworkflows/local/sv_sniffles.nf | 23 ++++++++ subworkflows/local/vc_medaka.nf | 42 ++++++++++++++ workflows/nanoseq.nf | 57 +++++++++++++++---- 16 files changed, 359 insertions(+), 22 deletions(-) create mode 100644 conf/test_nobc_nodx_vc.config create mode 100644 modules/local/medaka_variant.nf create mode 100644 modules/local/samtools_sort_index.nf create mode 100644 modules/local/sniffles.nf create mode 100644 subworkflows/local/bam_sort_index_samtools.nf create mode 100644 subworkflows/local/sv_sniffles.nf create mode 100644 subworkflows/local/vc_medaka.nf diff --git a/conf/modules.config b/conf/modules.config index 4bfd2566..ab9531fd 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -69,6 +69,15 @@ params { publish_files = ['stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats', 'bam':'bam', 'bai':'bam'] publish_dir = "${params.aligner}" } + 'medaka_vc' { + publish_dir = "${params.aligner}" + publish_files = ['.vcf':'medaka'] + args = '-s r941_prom_hac_snp_g507 -m r941_prom_hac_variant_g507' + } + 'sniffles_sv' { + publish_dir = "${params.aligner}" + publish_files = ['.vcf':'sniffles'] + } 'bedtools_genomecov' { publish_files = false } diff --git a/conf/test_full.config b/conf/test_full.config index 06d846dd..52ce3ec1 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -10,13 +10,13 @@ ---------------------------------------------------------------------------------------- */ -params { - config_profile_name = 'Full test profile' - config_profile_description = 'Full test dataset to check pipeline function' - - // Input data for full size test - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/nanoseq/samplesheet_full.csv' - protocol = 'cDNA' - skip_basecalling = true - skip_demultiplexing = true +params { + config_profile_name = 'Full test profile' + config_profile_description = 'Full test dataset to check pipeline function' + + // Input data for full size test + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/nanoseq/samplesheet_full.csv' + protocol = 'cDNA' + skip_basecalling = true + skip_demultiplexing = true } \ No newline at end of file diff --git a/conf/test_nobc_nodx_vc.config b/conf/test_nobc_nodx_vc.config new file mode 100644 index 00000000..b8463f9a --- /dev/null +++ b/conf/test_nobc_nodx_vc.config @@ -0,0 +1,26 @@ +/* + * ------------------------------------------------- + * Nextflow config file for running tests + * ------------------------------------------------- + * Defines bundled input files and everything required + * to run a fast and simple test. Use as follows: + * nextflow run nf-core/nanoseq -profile test_nobc_nodx_vc, + */ + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset to check variant calling functions' + + // Limit resources so that this can run on Travis + max_cpus = 2 + max_memory = 6.GB + max_time = 12.h + + // Input data to skip basecalling and demultiplexing, and variant call + input = 'https://raw.githubusercontent.com//christopher-hakkaart/testdata/main/samplesheet_nobc_vc.csv' + protocol = 'DNA' + skip_basecalling = true + skip_quantification = true + skip_demultiplexing = true + call_variants = true +} diff --git a/modules/local/medaka_variant.nf b/modules/local/medaka_variant.nf new file mode 100644 index 00000000..89009664 --- /dev/null +++ b/modules/local/medaka_variant.nf @@ -0,0 +1,49 @@ +// Import generic module functions +include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' + +params.options = [:] +def options = initOptions(params.options) + +process MEDAKA { + tag "$meta.id" + label 'process_high' + publishDir "${params.outdir}", + mode: params.publish_dir_mode, + saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:meta.id) } + + conda (params.enable_conda ? "bioconda::medaka=1.4.4" : null) + if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { + container 'https://depot.galaxyproject.org/singularity/medaka:1.4.4--py38h130def0_0' + } else { + container 'quay.io/biocontainers/medaka:1.4.4--py38h130def0_0' + } + + input: + tuple val(meta), path(sizes), val(is_transcripts), path(bam), path(bai) // + path(fasta) + + output: + path ("${meta.id}/round_1.vcf") , emit: variant_calls // vcf files + path "versions.yml" , emit: versions + + script: + def args = options.args ?: '' + def split_mnps = params.split_mnps ? "-l" : '' + def phase_vcf = params.phase_vcf ? "-p" : '' + """ + medaka_variant \\ + -d \\ + -f $fasta \\ + -i $bam \\ + -o ${meta.id}/ \\ + -t $task.cpus \\ + $split_mnps \\ + $phase_vcf \\ + $args + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + medaka: \$( medaka --version 2>&1 | sed 's/medaka //g' ) + END_VERSIONS + """ +} diff --git a/modules/local/minimap2_align.nf b/modules/local/minimap2_align.nf index 0ad6a729..5c63ea7d 100644 --- a/modules/local/minimap2_align.nf +++ b/modules/local/minimap2_align.nf @@ -30,12 +30,14 @@ process MINIMAP2_ALIGN { def kmer = (params.protocol == 'directRNA') ? "-k14" : "" def stranded = (params.stranded || params.protocol == 'directRNA') ? "-uf" : "" def junctions = (params.protocol != 'DNA' && bed) ? "--junc-bed ${file(bed)}" : "" + def md = (params.call_variants && params.protocol == 'DNA') ? "--MD" : "" """ minimap2 \\ $preset \\ $kmer \\ $stranded \\ $junctions \\ + $md \\ -t $task.cpus \\ $index \\ $fastq > ${meta.id}.sam diff --git a/modules/local/samtools_sort_index.nf b/modules/local/samtools_sort_index.nf new file mode 100644 index 00000000..cd625e23 --- /dev/null +++ b/modules/local/samtools_sort_index.nf @@ -0,0 +1,41 @@ +// Import generic module functions +include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' + +params.options = [:] +def options = initOptions(params.options) + +process SAMTOOLS_SORT_INDEX { + tag "$meta.id" + label 'process_low' + publishDir "${params.outdir}", + mode: params.publish_dir_mode, + saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } + + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { + container "https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0" + } else { + container "quay.io/biocontainers/samtools:1.14--hb421002_0" + } + + input: + tuple val(meta), path(bam) + + output: + tuple val(meta), path("*sorted.bam"), path("*.bai") , optional:true, emit: bam_bai + tuple val(meta), path("*sorted.bam"), path("*.csi") , optional:true, emit: bam_csi + path "versions.yml" , emit: versions + + script: + def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + """ + samtools sort $options.args -@ $task.cpus -o ${prefix}.sorted.bam -T $prefix $bam + + samtools index $options.args ${prefix}.sorted.bam + + cat <<-END_VERSIONS > versions.yml + ${getProcessName(task.process)}: + ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ +} \ No newline at end of file diff --git a/modules/local/sniffles.nf b/modules/local/sniffles.nf new file mode 100644 index 00000000..c07e3603 --- /dev/null +++ b/modules/local/sniffles.nf @@ -0,0 +1,42 @@ +// Import generic module functions +include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' + +params.options = [:] +def options = initOptions(params.options) + +process SNIFFLES { + tag "$meta.id" + label 'process_high' + publishDir "${params.outdir}", + mode: params.publish_dir_mode, + saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:meta.id) } + + conda (params.enable_conda ? "bioconda::sniffles=1.0.12" : null) + if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { + container 'https://depot.galaxyproject.org/singularity/sniffles:1.0.12--h8b12597_1' + } else { + container 'quay.io/biocontainers/sniffles:1.0.12--h8b12597_1' + } + + input: + tuple val(meta), path(sizes), val(is_transcripts), path(bam), path(bai) + + + output: + path "*_sniffles.vcf" , emit: sv_calls // vcf files + path "versions.yml" , emit: versions + + + script: + """ + sniffles \ + -m $bam \ + -v ${meta.id}_sniffles.vcf \ + -t $task.cpus + + cat <<-END_VERSIONS > versions.yml + ${getProcessName(task.process)}: + ${getSoftwareName(task.process)}: \$( echo \$(featureCounts -v 2>&1) | sed -e "s/featureCounts v//g") + END_VERSIONS + """ +} \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index 76de0fdc..be766fbc 100644 --- a/nextflow.config +++ b/nextflow.config @@ -41,6 +41,11 @@ params { save_align_intermeds = false skip_alignment = false + // Options: Variant calling + call_variants = false + split_mnps = false + phase_vcf = false + // Options: Visualisation skip_bigbed = false skip_bigwig = false @@ -178,6 +183,7 @@ profiles { test_nobc_dx { includeConfig 'conf/test_nobc_dx.config' } test_nobc_nodx { includeConfig 'conf/test_nobc_nodx.config' } test_nobc_nodx_noaln { includeConfig 'conf/test_nobc_nodx_noaln.config' } + test_nobc_nodx_vc { includeConfig 'conf/test_nobc_nodx_vc.config' } } // Export these variables to prevent local Python/R libraries from conflicting with those in the container diff --git a/nextflow_schema.json b/nextflow_schema.json index 4d046201..bc80f2fe 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -187,6 +187,29 @@ } } }, + "variant_calling_options": { + "title": "Variant calling options", + "description": "Options to adjust pameters and filtering criteria for varinat calling.", + "default": "", + "fa_icon": "fas fa-align-center", + "properties": { + "call_variants": { + "type": "boolean", + "description": "Specifies if variants will be called.", + "fa_icon": "fas fa-exchange-alt" + }, + "split_mnps": { + "type": "boolean", + "description": "Specifies if MNPs will be split into SNPs.", + "fa_icon": "fas fa-file-alt" + }, + "phase_vcf": { + "type": "boolean", + "fa_icon": "fas fa-exchange-alt", + "description": "Specifies if vcf will be phased." + } + } + }, "differential_analysis_options": { "title": "Differential analysis options", "description": "Options to adjust quantification and differential analysis", diff --git a/subworkflows/local/align_graphmap2.nf b/subworkflows/local/align_graphmap2.nf index 858cf057..7e736e00 100644 --- a/subworkflows/local/align_graphmap2.nf +++ b/subworkflows/local/align_graphmap2.nf @@ -28,7 +28,7 @@ workflow ALIGN_GRAPHMAP2 { .collate(13) .map { it -> [ it[6], it[7], it[0], it[1], it[2], it[3], it[10], it[4] ] } // [ sample, fastq, fasta, sizes, gtf, bed, is_transcripts, index ] .set { ch_index } - + /* * Map reads with GRAPHMAP2 */ @@ -36,6 +36,7 @@ workflow ALIGN_GRAPHMAP2 { ch_align_sam = GRAPHMAP2_ALIGN.out.align_sam emit: + ch_index graphmap2_version ch_align_sam } diff --git a/subworkflows/local/align_minimap2.nf b/subworkflows/local/align_minimap2.nf index 667a1c35..8e1598a8 100644 --- a/subworkflows/local/align_minimap2.nf +++ b/subworkflows/local/align_minimap2.nf @@ -36,6 +36,7 @@ workflow ALIGN_MINIMAP2 { ch_align_sam = MINIMAP2_ALIGN.out.align_sam emit: + ch_index minimap2_version ch_align_sam } diff --git a/subworkflows/local/bam_sort_index_samtools.nf b/subworkflows/local/bam_sort_index_samtools.nf new file mode 100644 index 00000000..75e50bee --- /dev/null +++ b/subworkflows/local/bam_sort_index_samtools.nf @@ -0,0 +1,37 @@ +/* + * Sort, index BAM file and run samtools stats, flagstat and idxstats + */ + +params.options = [:] + +include { SAMTOOLS_VIEW_BAM } from '../../modules/local/samtools_view_bam' addParams( options: params.options ) +include { SAMTOOLS_SORT_INDEX } from '../../modules/local/samtools_sort_index' addParams( options: params.options ) +include { BAM_STATS_SAMTOOLS } from '../../subworkflows/nf-core/bam_stats_samtools' addParams( options: params.options ) + +workflow BAM_SORT_INDEX_SAMTOOLS { + take: + ch_sam // channel: [ val(meta), [ bam ] ] + + main: + SAMTOOLS_VIEW_BAM ( ch_sam ) + + SAMTOOLS_SORT_INDEX( SAMTOOLS_VIEW_BAM.out.bam ) + + ch_sam + .join( SAMTOOLS_SORT_INDEX.out.bam_bai ) + .map { it -> [ it[0], it[1], it[2], it[4], it[5] ] } + .set { sortbam } + + BAM_STATS_SAMTOOLS ( SAMTOOLS_SORT_INDEX.out.bam_bai ) + BAM_STATS_SAMTOOLS.out.stats + .join ( BAM_STATS_SAMTOOLS.out.idxstats ) + .join ( BAM_STATS_SAMTOOLS.out.flagstat ) + .map { it -> [ it[1], it[2], it[3] ] } + .set { sortbam_stats_multiqc } + versions = BAM_STATS_SAMTOOLS.out.versions + + emit: + sortbam + sortbam_stats_multiqc + versions +} diff --git a/subworkflows/local/quantify_stringtie_featurecounts.nf b/subworkflows/local/quantify_stringtie_featurecounts.nf index 29359847..bab6e6b1 100644 --- a/subworkflows/local/quantify_stringtie_featurecounts.nf +++ b/subworkflows/local/quantify_stringtie_featurecounts.nf @@ -5,7 +5,7 @@ params.stringtie2_options = [:] params.featurecounts_options = [:] -include { STRINGTIE2 } from '../../modules/local/stringtie2' addParams( options: params.stringtie2_options ) +include { STRINGTIE2 } from '../../modules/local/stringtie2' addParams( options: params.stringtie2_options ) include { STRINGTIE_MERGE } from '../../modules/nf-core/modules/stringtie/merge/main' addParams( options: params.stringtie2_options ) include { SUBREAD_FEATURECOUNTS } from '../../modules/local/subread_featurecounts' addParams( options: params.featurecounts_options ) diff --git a/subworkflows/local/sv_sniffles.nf b/subworkflows/local/sv_sniffles.nf new file mode 100644 index 00000000..ab32e66f --- /dev/null +++ b/subworkflows/local/sv_sniffles.nf @@ -0,0 +1,23 @@ +/* + * Structural variant calling with SNIFFLES + */ + +params.sniffles_sv_options = [:] + +include { SNIFFLES } from '../../modules/local/sniffles' addParams( options: params.sniffles_sv_options ) + +workflow SV_SNIFFLES { + take: + ch_view_sortbam + + main: + /* + * Call variants with MEDAKA + */ + SNIFFLES( ch_view_sortbam ) + ch_sv_calls = SNIFFLES.out.sv_calls + + emit: + ch_sv_calls + +} \ No newline at end of file diff --git a/subworkflows/local/vc_medaka.nf b/subworkflows/local/vc_medaka.nf new file mode 100644 index 00000000..3918162a --- /dev/null +++ b/subworkflows/local/vc_medaka.nf @@ -0,0 +1,42 @@ +/* + * Small variant calling with MEDAKA + */ + +params.medaka_vc_options = [:] + +include { MEDAKA } from '../../modules/local/medaka_variant' addParams( options: params.medaka_vc_options ) + +workflow VC_MEDAKA { + take: + ch_view_sortbam // + ch_index // + + main: + /* + * Split into a different channel for each chromosome + */ + // TODO Add module that cuts chromosomes from reference to use as regions to split variant calling + //SPLIT_CHROM( ch_view_sortbam ) + //.splitCsv() + //.combine( ch_view_sortbam ) // + //.unique() + //.map { it -> [ it[1], it[4], it[5], it[0] ] } // + //.map{ meta, bam, bai, chrom -> + //new_meta = meta.clone() + //new_meta.chrom = chrom + //[new_meta, bam, bai] + //}.set{ch_bam_vc_chrom} + + /* + * Call variants with MEDAKA + */ + MEDAKA( ch_view_sortbam, ch_index ) + ch_variant_calls = MEDAKA.out.variant_calls + + + // TODO Add module to concatenate variant calls back togehter and index + + emit: + ch_variant_calls + +} \ No newline at end of file diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index bbf0475f..3d20555d 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -106,8 +106,8 @@ def qcat_options = modules['qcat'] def nanolyse_options = modules['nanolyse'] def bambu_options = modules['bambu'] -include { GET_TEST_DATA } from '../modules/local/get_test_data' addParams( options: [:] ) -include { GET_NANOLYSE_FASTA } from '../modules/local/get_nanolyse_fasta' addParams( options: [:] ) +include { GET_TEST_DATA } from '../modules/local/get_test_data' addParams( options: [:] ) +include { GET_NANOLYSE_FASTA } from '../modules/local/get_nanolyse_fasta' addParams( options: [:] ) include { GUPPY } from '../modules/local/guppy' addParams( options: guppy_options ) include { QCAT } from '../modules/local/qcat' addParams( options: qcat_options ) include { BAM_RENAME } from '../modules/local/bam_rename' addParams( options: [:] ) @@ -126,6 +126,8 @@ def graphmap2_index_options = modules['graphmap2_index'] def graphmap2_align_options = modules['graphmap2_align'] def minimap2_index_options = modules['minimap2_index'] def minimap2_align_options = modules['minimap2_align'] +def medaka_vc_options = modules['medaka_vc'] +def sniffles_sv_options = modules['sniffles_sv'] def samtools_sort_options = modules['samtools_sort'] def bigwig_options = modules['ucsc_bedgraphtobigwig'] def bigbed_options = modules['ucsc_bed12tobigbed'] @@ -139,6 +141,9 @@ include { PREPARE_GENOME } from '../subworkflows/local/prepare include { ALIGN_GRAPHMAP2 } from '../subworkflows/local/align_graphmap2' addParams( index_options: graphmap2_index_options, align_options: graphmap2_align_options ) include { ALIGN_MINIMAP2 } from '../subworkflows/local/align_minimap2' addParams( index_options: minimap2_index_options, align_options: minimap2_align_options ) include { BAM_SORT_SAMTOOLS } from '../subworkflows/local/bam_sort_samtools' addParams( samtools_options: samtools_sort_options ) +include { BAM_SORT_INDEX_SAMTOOLS } from '../subworkflows/local/bam_sort_index_samtools' addParams( samtools_options: samtools_sort_options ) +include { VC_MEDAKA } from '../subworkflows/local/vc_medaka' addParams( medaka_vc_options: medaka_vc_options ) +include { SV_SNIFFLES } from '../subworkflows/local/sv_sniffles' addParams( sniffles_sv_options: sniffles_sv_options ) include { BEDTOOLS_UCSC_BIGWIG } from '../subworkflows/local/bedtools_ucsc_bigwig' addParams( bigwig_options: bigwig_options ) include { BEDTOOLS_UCSC_BIGBED } from '../subworkflows/local/bedtools_ucsc_bigbed' addParams( bigbed_options: bigbed_options ) include { QUANTIFY_STRINGTIE_FEATURECOUNTS } from '../subworkflows/local/quantify_stringtie_featurecounts' addParams( stringtie2_options: stringtie2_options, featurecounts_options: featurecounts_options ) @@ -304,12 +309,12 @@ workflow NANOSEQ{ ch_software_versions = ch_software_versions.mix(PREPARE_GENOME.out.samtools_version.first().ifEmpty(null)) ch_software_versions = ch_software_versions.mix(PREPARE_GENOME.out.gtf2bed_version.first().ifEmpty(null)) if (params.aligner == 'minimap2') { - /* * SUBWORKFLOW: Align fastq files with minimap2 and sort bam files */ ALIGN_MINIMAP2 ( ch_fasta_index, ch_fastq ) ch_align_sam = ALIGN_MINIMAP2.out.ch_align_sam + ch_index = ALIGN_MINIMAP2.out.ch_index ch_software_versions = ch_software_versions.mix(ALIGN_MINIMAP2.out.minimap2_version.first().ifEmpty(null)) } else { @@ -318,17 +323,47 @@ workflow NANOSEQ{ */ ALIGN_GRAPHMAP2 ( ch_fasta_index, ch_fastq ) ch_align_sam = ALIGN_GRAPHMAP2.out.ch_align_sam + ch_index = ALIGN_GRAPHMAP2.out.ch_index ch_software_versions = ch_software_versions.mix(ALIGN_GRAPHMAP2.out.graphmap2_version.first().ifEmpty(null)) } - //if (DNA structural variant){ - // Your merged samtools sort and index module - //}else{ - BAM_SORT_SAMTOOLS ( ch_align_sam ) - ch_view_sortbam = BAM_SORT_SAMTOOLS.out.sortbam - ch_software_versions = ch_software_versions.mix(BAM_SORT_SAMTOOLS.out.versions.first().ifEmpty(null)) - ch_samtools_multiqc = BAM_SORT_SAMTOOLS.out.sortbam_stats_multiqc.ifEmpty([]) - //} + if (params.call_variants) { + /* + * SUBWORKFLOW: View, then sort, and index bam files using one module + */ + BAM_SORT_INDEX_SAMTOOLS ( ch_align_sam ) + ch_view_sortbam = BAM_SORT_INDEX_SAMTOOLS.out.sortbam + ch_software_versions = ch_software_versions.mix(BAM_SORT_INDEX_SAMTOOLS.out.versions.first().ifEmpty(null)) + ch_samtools_multiqc = BAM_SORT_INDEX_SAMTOOLS.out.sortbam_stats_multiqc.ifEmpty([]) + } else { + + /* + * SUBWORKFLOW: View, then sort and index bam files using two modules + */ + BAM_SORT_SAMTOOLS ( ch_align_sam ) + ch_view_sortbam = BAM_SORT_SAMTOOLS.out.sortbam + ch_software_versions = ch_software_versions.mix(BAM_SORT_SAMTOOLS.out.versions.first().ifEmpty(null)) + ch_samtools_multiqc = BAM_SORT_SAMTOOLS.out.sortbam_stats_multiqc.ifEmpty([]) + } + + + if (params.call_variants && params.protocol == 'DNA') { + /* + * SUBWORKFLOW: Call variants with medaka + */ + ch_medaka_version = Channel.empty() + VC_MEDAKA ( ch_view_sortbam, ch_index.map{ it [2] } ) + ch_medaka_vc = VC_MEDAKA.out.ch_variant_calls + } + + if (params.call_variants && params.protocol == 'DNA') { + /* + * SUBWORKFLOW: Call structural variants with sniffles + */ + ch_sniffles_version = Channel.empty() + SV_SNIFFLES ( ch_view_sortbam ) + ch_sniffles_sv = SV_SNIFFLES.out.ch_sv_calls + } ch_bedtools_version = Channel.empty() if (!params.skip_bigwig){ From c92e207bd9c2bbc50785ede7a35836589b0119d7 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Mon, 13 Dec 2021 21:01:30 +0800 Subject: [PATCH 08/43] put all samtools in one subworkflow and fix linting errors --- modules/local/medaka_variant.nf | 2 +- modules/local/samtools_sort_index.nf | 3 +- modules/local/sniffles.nf | 3 +- nextflow_schema.json | 5 ++- subworkflows/local/align_graphmap2.nf | 2 +- subworkflows/local/bam_sort_index_samtools.nf | 32 ++++++++++----- subworkflows/local/bam_sort_samtools.nf | 40 ------------------- subworkflows/local/sv_sniffles.nf | 5 ++- subworkflows/local/vc_medaka.nf | 14 +++---- workflows/.Rhistory | 0 workflows/nanoseq.nf | 31 +++++--------- 11 files changed, 51 insertions(+), 86 deletions(-) delete mode 100644 subworkflows/local/bam_sort_samtools.nf create mode 100644 workflows/.Rhistory diff --git a/modules/local/medaka_variant.nf b/modules/local/medaka_variant.nf index 89009664..38f32d1b 100644 --- a/modules/local/medaka_variant.nf +++ b/modules/local/medaka_variant.nf @@ -19,7 +19,7 @@ process MEDAKA { } input: - tuple val(meta), path(sizes), val(is_transcripts), path(bam), path(bai) // + tuple val(meta), path(sizes), val(is_transcripts), path(bam), path(bai) // path(fasta) output: diff --git a/modules/local/samtools_sort_index.nf b/modules/local/samtools_sort_index.nf index cd625e23..f16c2906 100644 --- a/modules/local/samtools_sort_index.nf +++ b/modules/local/samtools_sort_index.nf @@ -38,4 +38,5 @@ process SAMTOOLS_SORT_INDEX { ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ -} \ No newline at end of file +} + diff --git a/modules/local/sniffles.nf b/modules/local/sniffles.nf index c07e3603..317eede5 100644 --- a/modules/local/sniffles.nf +++ b/modules/local/sniffles.nf @@ -39,4 +39,5 @@ process SNIFFLES { ${getSoftwareName(task.process)}: \$( echo \$(featureCounts -v 2>&1) | sed -e "s/featureCounts v//g") END_VERSIONS """ -} \ No newline at end of file +} + diff --git a/nextflow_schema.json b/nextflow_schema.json index bc80f2fe..516b2c01 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -499,6 +499,9 @@ { "$ref": "#/definitions/alignment_options" }, + { + "$ref": "#/definitions/variant_calling_options" + }, { "$ref": "#/definitions/differential_analysis_options" }, @@ -518,4 +521,4 @@ "$ref": "#/definitions/generic_options" } ] -} \ No newline at end of file +} diff --git a/subworkflows/local/align_graphmap2.nf b/subworkflows/local/align_graphmap2.nf index 7e736e00..6723214e 100644 --- a/subworkflows/local/align_graphmap2.nf +++ b/subworkflows/local/align_graphmap2.nf @@ -28,7 +28,7 @@ workflow ALIGN_GRAPHMAP2 { .collate(13) .map { it -> [ it[6], it[7], it[0], it[1], it[2], it[3], it[10], it[4] ] } // [ sample, fastq, fasta, sizes, gtf, bed, is_transcripts, index ] .set { ch_index } - + /* * Map reads with GRAPHMAP2 */ diff --git a/subworkflows/local/bam_sort_index_samtools.nf b/subworkflows/local/bam_sort_index_samtools.nf index 75e50bee..c7ecc2b5 100644 --- a/subworkflows/local/bam_sort_index_samtools.nf +++ b/subworkflows/local/bam_sort_index_samtools.nf @@ -4,25 +4,37 @@ params.options = [:] -include { SAMTOOLS_VIEW_BAM } from '../../modules/local/samtools_view_bam' addParams( options: params.options ) +include { SAMTOOLS_VIEW_BAM } from '../../modules/local/samtools_view_bam' addParams( options: params.options ) +include { SAMTOOLS_SORT } from '../../modules/nf-core/modules/samtools/sort/main' addParams( options: params.options ) +include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' addParams( options: params.options ) include { SAMTOOLS_SORT_INDEX } from '../../modules/local/samtools_sort_index' addParams( options: params.options ) -include { BAM_STATS_SAMTOOLS } from '../../subworkflows/nf-core/bam_stats_samtools' addParams( options: params.options ) +include { BAM_STATS_SAMTOOLS } from '../../subworkflows/nf-core/bam_stats_samtools' addParams( options: params.options ) workflow BAM_SORT_INDEX_SAMTOOLS { take: ch_sam // channel: [ val(meta), [ bam ] ] + call_variants main: SAMTOOLS_VIEW_BAM ( ch_sam ) + if ( call_variants ) { + SAMTOOLS_SORT_INDEX ( SAMTOOLS_VIEW_BAM.out.bam ) + ch_sam + .join( SAMTOOLS_SORT_INDEX.out.bam_bai ) + .map { it -> [ it[0], it[1], it[2], it[4], it[5] ] } + .set { sortbam } + BAM_STATS_SAMTOOLS ( SAMTOOLS_SORT_INDEX.out.bam_bai ) + } else{ + SAMTOOLS_SORT ( SAMTOOLS_VIEW_BAM.out.bam ) + SAMTOOLS_INDEX ( SAMTOOLS_SORT.out.bam ) + ch_sam + .join( SAMTOOLS_SORT.out.bam ) + .join( SAMTOOLS_INDEX.out.bai ) + .map { it -> [ it[0], it[1], it[2], it[4], it[5] ] } + .set { sortbam } + BAM_STATS_SAMTOOLS ( SAMTOOLS_SORT.out.bam.join(SAMTOOLS_INDEX.out.bai, by: [0]) ) + } - SAMTOOLS_SORT_INDEX( SAMTOOLS_VIEW_BAM.out.bam ) - - ch_sam - .join( SAMTOOLS_SORT_INDEX.out.bam_bai ) - .map { it -> [ it[0], it[1], it[2], it[4], it[5] ] } - .set { sortbam } - - BAM_STATS_SAMTOOLS ( SAMTOOLS_SORT_INDEX.out.bam_bai ) BAM_STATS_SAMTOOLS.out.stats .join ( BAM_STATS_SAMTOOLS.out.idxstats ) .join ( BAM_STATS_SAMTOOLS.out.flagstat ) diff --git a/subworkflows/local/bam_sort_samtools.nf b/subworkflows/local/bam_sort_samtools.nf deleted file mode 100644 index 83c2d059..00000000 --- a/subworkflows/local/bam_sort_samtools.nf +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Sort, index BAM file and run samtools stats, flagstat and idxstats - */ - -params.options = [:] - -include { SAMTOOLS_VIEW_BAM } from '../../modules/local/samtools_view_bam' addParams( options: params.options ) -include { SAMTOOLS_SORT } from '../../modules/nf-core/modules/samtools/sort/main' addParams( options: params.options ) -include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' addParams( options: params.options ) -include { BAM_STATS_SAMTOOLS } from '../../subworkflows/nf-core/bam_stats_samtools' addParams( options: params.options ) - -workflow BAM_SORT_SAMTOOLS { - take: - ch_sam // channel: [ val(meta), [ bam ] ] - - main: - SAMTOOLS_VIEW_BAM ( ch_sam ) - - SAMTOOLS_SORT ( SAMTOOLS_VIEW_BAM.out.bam ) - SAMTOOLS_INDEX ( SAMTOOLS_SORT.out.bam ) - - ch_sam - .join( SAMTOOLS_SORT.out.bam ) - .join( SAMTOOLS_INDEX.out.bai ) - .map { it -> [ it[0], it[1], it[2], it[4], it[5] ] } - .set { sortbam } - - BAM_STATS_SAMTOOLS ( SAMTOOLS_SORT.out.bam.join(SAMTOOLS_INDEX.out.bai, by: [0]) ) - BAM_STATS_SAMTOOLS.out.stats - .join ( BAM_STATS_SAMTOOLS.out.idxstats ) - .join ( BAM_STATS_SAMTOOLS.out.flagstat ) - .map { it -> [ it[1], it[2], it[3] ] } - .set { sortbam_stats_multiqc } - versions = BAM_STATS_SAMTOOLS.out.versions - - emit: - sortbam - sortbam_stats_multiqc - versions -} diff --git a/subworkflows/local/sv_sniffles.nf b/subworkflows/local/sv_sniffles.nf index ab32e66f..9f29cb45 100644 --- a/subworkflows/local/sv_sniffles.nf +++ b/subworkflows/local/sv_sniffles.nf @@ -8,7 +8,7 @@ include { SNIFFLES } from '../../modules/local/sniffles' a workflow SV_SNIFFLES { take: - ch_view_sortbam + ch_view_sortbam main: /* @@ -20,4 +20,5 @@ workflow SV_SNIFFLES { emit: ch_sv_calls -} \ No newline at end of file +} + diff --git a/subworkflows/local/vc_medaka.nf b/subworkflows/local/vc_medaka.nf index 3918162a..742f3568 100644 --- a/subworkflows/local/vc_medaka.nf +++ b/subworkflows/local/vc_medaka.nf @@ -8,20 +8,20 @@ include { MEDAKA } from '../../modules/local/medaka_variant' workflow VC_MEDAKA { take: - ch_view_sortbam // - ch_index // + ch_view_sortbam // + ch_index // main: /* * Split into a different channel for each chromosome */ - // TODO Add module that cuts chromosomes from reference to use as regions to split variant calling + // TODO Add module that cuts chromosomes from reference to use as regions to split variant calling //SPLIT_CHROM( ch_view_sortbam ) //.splitCsv() //.combine( ch_view_sortbam ) // //.unique() //.map { it -> [ it[1], it[4], it[5], it[0] ] } // - //.map{ meta, bam, bai, chrom -> + //.map{ meta, bam, bai, chrom -> //new_meta = meta.clone() //new_meta.chrom = chrom //[new_meta, bam, bai] @@ -29,14 +29,14 @@ workflow VC_MEDAKA { /* * Call variants with MEDAKA - */ + */ MEDAKA( ch_view_sortbam, ch_index ) ch_variant_calls = MEDAKA.out.variant_calls // TODO Add module to concatenate variant calls back togehter and index - + emit: ch_variant_calls -} \ No newline at end of file +} diff --git a/workflows/.Rhistory b/workflows/.Rhistory new file mode 100644 index 00000000..e69de29b diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index 3d20555d..def70ac5 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -140,7 +140,6 @@ include { INPUT_CHECK } from '../subworkflows/local/input_c include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome' addParams( genome_options: genome_options ) include { ALIGN_GRAPHMAP2 } from '../subworkflows/local/align_graphmap2' addParams( index_options: graphmap2_index_options, align_options: graphmap2_align_options ) include { ALIGN_MINIMAP2 } from '../subworkflows/local/align_minimap2' addParams( index_options: minimap2_index_options, align_options: minimap2_align_options ) -include { BAM_SORT_SAMTOOLS } from '../subworkflows/local/bam_sort_samtools' addParams( samtools_options: samtools_sort_options ) include { BAM_SORT_INDEX_SAMTOOLS } from '../subworkflows/local/bam_sort_index_samtools' addParams( samtools_options: samtools_sort_options ) include { VC_MEDAKA } from '../subworkflows/local/vc_medaka' addParams( medaka_vc_options: medaka_vc_options ) include { SV_SNIFFLES } from '../subworkflows/local/sv_sniffles' addParams( sniffles_sv_options: sniffles_sv_options ) @@ -327,27 +326,15 @@ workflow NANOSEQ{ ch_software_versions = ch_software_versions.mix(ALIGN_GRAPHMAP2.out.graphmap2_version.first().ifEmpty(null)) } - if (params.call_variants) { - /* - * SUBWORKFLOW: View, then sort, and index bam files using one module - */ - BAM_SORT_INDEX_SAMTOOLS ( ch_align_sam ) - ch_view_sortbam = BAM_SORT_INDEX_SAMTOOLS.out.sortbam - ch_software_versions = ch_software_versions.mix(BAM_SORT_INDEX_SAMTOOLS.out.versions.first().ifEmpty(null)) - ch_samtools_multiqc = BAM_SORT_INDEX_SAMTOOLS.out.sortbam_stats_multiqc.ifEmpty([]) - } else { - - /* - * SUBWORKFLOW: View, then sort and index bam files using two modules - */ - BAM_SORT_SAMTOOLS ( ch_align_sam ) - ch_view_sortbam = BAM_SORT_SAMTOOLS.out.sortbam - ch_software_versions = ch_software_versions.mix(BAM_SORT_SAMTOOLS.out.versions.first().ifEmpty(null)) - ch_samtools_multiqc = BAM_SORT_SAMTOOLS.out.sortbam_stats_multiqc.ifEmpty([]) - } - + /* + * SUBWORKFLOW: View, then sort, and index bam files + */ + BAM_SORT_INDEX_SAMTOOLS ( ch_align_sam, params.call_variants ) + ch_view_sortbam = BAM_SORT_INDEX_SAMTOOLS.out.sortbam + ch_software_versions = ch_software_versions.mix(BAM_SORT_INDEX_SAMTOOLS.out.versions.first().ifEmpty(null)) + ch_samtools_multiqc = BAM_SORT_INDEX_SAMTOOLS.out.sortbam_stats_multiqc.ifEmpty([]) - if (params.call_variants && params.protocol == 'DNA') { + if (params.call_variants && params.protocol == 'DNA') { /* * SUBWORKFLOW: Call variants with medaka */ @@ -356,7 +343,7 @@ workflow NANOSEQ{ ch_medaka_vc = VC_MEDAKA.out.ch_variant_calls } - if (params.call_variants && params.protocol == 'DNA') { + if (params.call_variants && params.protocol == 'DNA') { /* * SUBWORKFLOW: Call structural variants with sniffles */ From e5385c2892652b5ca07c3c40551bc68a26a916b1 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Mon, 13 Dec 2021 21:08:06 +0800 Subject: [PATCH 09/43] add vc check to GitHub Action --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb96db4c..d21b0f19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: NXF_ANSI_LOG: false strategy: matrix: - profiles: [test_bc_nodx, test_nobc_dx, test_nobc_nodx, test_nobc_nodx_noaln] + profiles: [test_bc_nodx, test_nobc_dx, test_nobc_nodx_vc, test_nobc_nodx, test_nobc_nodx_noaln] steps: - name: Check out pipeline code uses: actions/checkout@v2 From c8a574c62efc3fe0a42bb302a8c3937ce3349bde Mon Sep 17 00:00:00 2001 From: Christopher Hakkaart Date: Wed, 15 Dec 2021 14:48:39 +0100 Subject: [PATCH 10/43] Moved variant calling into one workflow --- nextflow.config | 4 +- nextflow_schema.json | 18 ++++++-- subworkflows/local/dna_variant_calling.nf | 56 +++++++++++++++++++++++ workflows/nanoseq.nf | 18 ++------ 4 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 subworkflows/local/dna_variant_calling.nf diff --git a/nextflow.config b/nextflow.config index be766fbc..26d0188f 100644 --- a/nextflow.config +++ b/nextflow.config @@ -41,10 +41,12 @@ params { save_align_intermeds = false skip_alignment = false - // Options: Variant calling + // Options: DNA variant calling call_variants = false split_mnps = false phase_vcf = false + skip_medaka = false + skip_sniffles = false // Options: Visualisation skip_bigbed = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 516b2c01..eca6b164 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -187,9 +187,9 @@ } } }, - "variant_calling_options": { - "title": "Variant calling options", - "description": "Options to adjust pameters and filtering criteria for varinat calling.", + "dna_variant_calling_options": { + "title": "DNA variant calling options", + "description": "Options to adjust pameters and filtering criteria for DNA varinat calling.", "default": "", "fa_icon": "fas fa-align-center", "properties": { @@ -207,6 +207,16 @@ "type": "boolean", "fa_icon": "fas fa-exchange-alt", "description": "Specifies if vcf will be phased." + }, + "skip_medaka": { + "type": "boolean", + "fa_icon": "fas fa-fast-forward", + "description": "Skip variant calling with sniffles." + }, + "skip_sniffles": { + "type": "boolean", + "fa_icon": "fas fa-fast-forward", + "description": "Skip variant calling with sniffles." } } }, @@ -500,7 +510,7 @@ "$ref": "#/definitions/alignment_options" }, { - "$ref": "#/definitions/variant_calling_options" + "$ref": "#/definitions/dna_variant_calling_options" }, { "$ref": "#/definitions/differential_analysis_options" diff --git a/subworkflows/local/dna_variant_calling.nf b/subworkflows/local/dna_variant_calling.nf new file mode 100644 index 00000000..91d42241 --- /dev/null +++ b/subworkflows/local/dna_variant_calling.nf @@ -0,0 +1,56 @@ +/* + * DNA variant calling + */ +params.medaka_vc_options = [:] +params.sniffles_sv_options = [:] + +include { MEDAKA } from '../../modules/local/medaka_variant' addParams( options: params.medaka_vc_options ) +include { SNIFFLES } from '../../modules/local/sniffles' addParams( options: params.sniffles_sv_options ) + +workflow DNA_VARIANT_CALLING { + + main: + take: + ch_view_sortbam + ch_index + skip_medaka + skip_sniffles + + main: + ch_variant_calls = Channel.empty() + if (!skip_medaka){ + /* + * Split into a different channel for each chromosome + */ + // TODO Add module that cuts chromosomes from reference to use as regions to split variant calling + //SPLIT_CHROM( ch_view_sortbam ) + //.splitCsv() + //.combine( ch_view_sortbam ) // + //.unique() + //.map { it -> [ it[1], it[4], it[5], it[0] ] } // + //.map{ meta, bam, bai, chrom -> + //new_meta = meta.clone() + //new_meta.chrom = chrom + //[new_meta, bam, bai] + //}.set{ch_bam_vc_chrom} + + /* + * Call variants with MEDAKA + */ + MEDAKA( ch_view_sortbam, ch_index ) + ch_variant_calls = MEDAKA.out.variant_calls + } + ch_sv_calls = Channel.empty() + if (!skip_sniffles){ + /* + * Call variants with SNIFFLES + */ + SNIFFLES( ch_view_sortbam ) + ch_sv_calls = SNIFFLES.out.sv_calls + } else{ + } + + emit: + ch_variant_calls + ch_sv_calls +} \ No newline at end of file diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index def70ac5..6b91b545 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -141,8 +141,7 @@ include { PREPARE_GENOME } from '../subworkflows/local/prepare include { ALIGN_GRAPHMAP2 } from '../subworkflows/local/align_graphmap2' addParams( index_options: graphmap2_index_options, align_options: graphmap2_align_options ) include { ALIGN_MINIMAP2 } from '../subworkflows/local/align_minimap2' addParams( index_options: minimap2_index_options, align_options: minimap2_align_options ) include { BAM_SORT_INDEX_SAMTOOLS } from '../subworkflows/local/bam_sort_index_samtools' addParams( samtools_options: samtools_sort_options ) -include { VC_MEDAKA } from '../subworkflows/local/vc_medaka' addParams( medaka_vc_options: medaka_vc_options ) -include { SV_SNIFFLES } from '../subworkflows/local/sv_sniffles' addParams( sniffles_sv_options: sniffles_sv_options ) +include { DNA_VARIANT_CALLING } from '../subworkflows/local/dna_variant_calling' addParams( medaka_vc_options: medaka_vc_options, sniffles_sv_options: sniffles_sv_options) include { BEDTOOLS_UCSC_BIGWIG } from '../subworkflows/local/bedtools_ucsc_bigwig' addParams( bigwig_options: bigwig_options ) include { BEDTOOLS_UCSC_BIGBED } from '../subworkflows/local/bedtools_ucsc_bigbed' addParams( bigbed_options: bigbed_options ) include { QUANTIFY_STRINGTIE_FEATURECOUNTS } from '../subworkflows/local/quantify_stringtie_featurecounts' addParams( stringtie2_options: stringtie2_options, featurecounts_options: featurecounts_options ) @@ -334,22 +333,13 @@ workflow NANOSEQ{ ch_software_versions = ch_software_versions.mix(BAM_SORT_INDEX_SAMTOOLS.out.versions.first().ifEmpty(null)) ch_samtools_multiqc = BAM_SORT_INDEX_SAMTOOLS.out.sortbam_stats_multiqc.ifEmpty([]) - if (params.call_variants && params.protocol == 'DNA') { + if (params.call_variants && params.protocol == 'DNA') { /* - * SUBWORKFLOW: Call variants with medaka + * SUBWORKFLOW: DNA variant calling */ ch_medaka_version = Channel.empty() - VC_MEDAKA ( ch_view_sortbam, ch_index.map{ it [2] } ) - ch_medaka_vc = VC_MEDAKA.out.ch_variant_calls - } - - if (params.call_variants && params.protocol == 'DNA') { - /* - * SUBWORKFLOW: Call structural variants with sniffles - */ ch_sniffles_version = Channel.empty() - SV_SNIFFLES ( ch_view_sortbam ) - ch_sniffles_sv = SV_SNIFFLES.out.ch_sv_calls + DNA_VARIANT_CALLING ( ch_view_sortbam, ch_index.map{ it [2] }, params.skip_medaka, params.skip_sniffles ) } ch_bedtools_version = Channel.empty() From dbcf7c61dfb696458b5057eacfa7d67e5b4c307b Mon Sep 17 00:00:00 2001 From: Christopher Hakkaart Date: Wed, 15 Dec 2021 14:54:45 +0100 Subject: [PATCH 11/43] Fix linting errors --- subworkflows/local/dna_variant_calling.nf | 8 ++++---- workflows/nanoseq.nf | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/dna_variant_calling.nf b/subworkflows/local/dna_variant_calling.nf index 91d42241..3ad02329 100644 --- a/subworkflows/local/dna_variant_calling.nf +++ b/subworkflows/local/dna_variant_calling.nf @@ -22,13 +22,13 @@ workflow DNA_VARIANT_CALLING { /* * Split into a different channel for each chromosome */ - // TODO Add module that cuts chromosomes from reference to use as regions to split variant calling + // TODO Add module that cuts chromosomes from reference to use as regions to split variant calling //SPLIT_CHROM( ch_view_sortbam ) //.splitCsv() //.combine( ch_view_sortbam ) // //.unique() //.map { it -> [ it[1], it[4], it[5], it[0] ] } // - //.map{ meta, bam, bai, chrom -> + //.map{ meta, bam, bai, chrom -> //new_meta = meta.clone() //new_meta.chrom = chrom //[new_meta, bam, bai] @@ -36,7 +36,7 @@ workflow DNA_VARIANT_CALLING { /* * Call variants with MEDAKA - */ + */ MEDAKA( ch_view_sortbam, ch_index ) ch_variant_calls = MEDAKA.out.variant_calls } @@ -53,4 +53,4 @@ workflow DNA_VARIANT_CALLING { emit: ch_variant_calls ch_sv_calls -} \ No newline at end of file +} diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index 6b91b545..3a49a654 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -333,7 +333,7 @@ workflow NANOSEQ{ ch_software_versions = ch_software_versions.mix(BAM_SORT_INDEX_SAMTOOLS.out.versions.first().ifEmpty(null)) ch_samtools_multiqc = BAM_SORT_INDEX_SAMTOOLS.out.sortbam_stats_multiqc.ifEmpty([]) - if (params.call_variants && params.protocol == 'DNA') { + if (params.call_variants && params.protocol == 'DNA') { /* * SUBWORKFLOW: DNA variant calling */ From 383f4662473c645a38f7e4e62e867da4f0f7a68c Mon Sep 17 00:00:00 2001 From: Christopher Hakkaart Date: Wed, 15 Dec 2021 15:54:44 +0100 Subject: [PATCH 12/43] Add citations --- CITATIONS.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CITATIONS.md b/CITATIONS.md index 1927b766..697c7f3c 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -26,6 +26,8 @@ * [Minimap2](https://pubmed.ncbi.nlm.nih.gov/29750242/) > Li H. Minimap2: pairwise alignment for nucleotide sequences. Bioinformatics. 2018 Sep 15;34(18):3094-3100. doi: 10.1093/bioinformatics/bty191. PMID: 29750242; PMCID: PMC6137996. +* [Medaka](https://github.com/nanoporetech/medaka) + * [MultiQC](https://www.ncbi.nlm.nih.gov/pubmed/27312411/) > Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. @@ -43,6 +45,9 @@ * [SAMtools](https://www.ncbi.nlm.nih.gov/pubmed/19505943/) > Li H, Handsaker B, Wysoker A, Fennell T, Ruan J, Homer N, Marth G, Abecasis G, Durbin R; 1000 Genome Project Data Processing Subgroup. The Sequence Alignment/Map format and SAMtools. Bioinformatics. 2009 Aug 15;25(16):2078-9. doi: 10.1093/bioinformatics/btp352. Epub 2009 Jun 8. PubMed PMID: 19505943; PubMed Central PMCID: PMC2723002. +* [Sniffles](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5990442/) + > Sedlazeck FJ, Rescheneder P, Smolka M, Fang H, Nattestad M, von Haeseler A, Schatz MC. Accurate detection of complex structural variations using single-molecule sequencing. Nat Methods. 2018 Jun;15(6):461-468. doi: 10 1038/s41592-018-0001-7. Epub 2018 Apr 30. PMID: 29713083; PMCID: PMC5990442. + * [StringTie2](https://www.ncbi.nlm.nih.gov/pubmed/31842956/) > Kovaka S, Zimin AV, Pertea GM, Razaghi R, Salzberg SL, Pertea M. Transcriptome assembly from long-read RNA-seq alignments with StringTie2 Genome Biol. 2019 Dec 16;20(1):278. doi: 10.1186/s13059-019-1910-1. PubMed PMID: 31842956; PubMed Central PMCID: PMC6912988. From bd8901e303c8c75fe031da5a1948d68f505cb2ed Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan <41866052+yuukiiwa@users.noreply.github.com> Date: Fri, 17 Dec 2021 00:19:28 +0800 Subject: [PATCH 13/43] Update nextflow_schema.json --- nextflow_schema.json | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index eca6b164..60dce87b 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -154,6 +154,7 @@ "fa_icon": "fas fa-fast-forward" }, "nanolyse_fasta": { + "type": "string", "description": "Fasta file to be filtered against using NanoLyse", "fa_icon": "far fa-file-code" } @@ -161,11 +162,13 @@ }, "alignment_options": { "title": "Alignment options", + "type": "object", "description": "Options to adjust parameters and filtering criteria for read alignments.", "default": "", "fa_icon": "fas fa-map-signs", "properties": { "aligner": { + "type": "string", "default": "minimap2", "description": "Specifies the aligner to use i.e. 'minimap2' or 'graphmap2'.", "fa_icon": "fas fa-align-center" @@ -182,6 +185,7 @@ "description": "Save the '.sam' files from the alignment step - not done by default." }, "skip_alignment": { + "type": "boolean", "description": "Skip alignment and downstream processes.", "fa_icon": "fas fa-fast-forward" } @@ -189,6 +193,7 @@ }, "dna_variant_calling_options": { "title": "DNA variant calling options", + "type": "object", "description": "Options to adjust pameters and filtering criteria for DNA varinat calling.", "default": "", "fa_icon": "fas fa-align-center", @@ -222,11 +227,13 @@ }, "differential_analysis_options": { "title": "Differential analysis options", + "type": "object", "description": "Options to adjust quantification and differential analysis", "default": "", "fa_icon": "fas fa-calculator", "properties": { "quantification_method": { + "type": "string", "default": "bambu", "description": "Specifies the transcript quantification method to use (available are: bambu or stringtie2). Only available when protocol is cDNA or directRNA.", "fa_icon": "fas fa-bezier-curve" @@ -332,12 +339,6 @@ "help_text": "If you're running offline, Nextflow will not be able to fetch the institutional config files from the internet. If you don't need them, then this is not a problem. If you do need them, you should download the files from the repo and tell Nextflow where to find them with this parameter.", "fa_icon": "fas fa-users-cog" }, - "hostnames": { - "type": "string", - "description": "Institutional configs hostname.", - "hidden": true, - "fa_icon": "fas fa-users-cog" - }, "config_profile_name": { "type": "string", "description": "Institutional config name.", @@ -412,22 +413,6 @@ "fa_icon": "fas fa-question-circle", "hidden": true }, - "publish_dir_mode": { - "type": "string", - "default": "copy", - "description": "Method used to save pipeline results to output directory.", - "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", - "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], - "hidden": true - }, "email_on_fail": { "type": "string", "description": "Email address for completion summary, only when pipeline fails.", @@ -488,13 +473,6 @@ "description": "Run this workflow with Conda. You can also use '-profile conda' instead of providing this parameter.", "hidden": true, "fa_icon": "fas fa-bacon" - }, - "singularity_pull_docker_container": { - "type": "boolean", - "description": "Instead of directly downloading Singularity images for use with Singularity, force the workflow to pull and convert Docker containers instead.", - "hidden": true, - "fa_icon": "fas fa-toolbox", - "help_text": "This may be useful for example if you are unable to directly pull Singularity containers to run the pipeline due to http/https proxy issues." } } } From 9c0b127add838abfbc506b46c4f523894e737fd6 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Fri, 17 Dec 2021 09:33:32 +0800 Subject: [PATCH 14/43] new dsl2 syntax (things run locally) --- .github/CONTRIBUTING.md | 38 +- .github/ISSUE_TEMPLATE/bug_report.md | 63 --- .github/ISSUE_TEMPLATE/bug_report.yml | 52 +++ .github/ISSUE_TEMPLATE/config.yml | 1 - .github/ISSUE_TEMPLATE/feature_request.md | 32 -- .github/ISSUE_TEMPLATE/feature_request.yml | 11 + .github/workflows/awsfulltest.yml | 7 +- .github/workflows/awstest.yml | 12 +- .github/workflows/ci.yml | 29 +- .github/workflows/linting_comment.yml | 1 + README.md | 14 +- assets/multiqc_config.yaml | 2 +- assets/nf-core-nanoseq_logo.png | Bin 18337 -> 0 bytes assets/nf-core-nanoseq_logo_light.png | 11 + assets/sendmail_template.txt | 4 +- bin/markdown_to_html.py | 91 ---- bin/scrape_software_versions.py | 36 -- conf/base.config | 3 + conf/modules.config | 399 ++++++++++++++---- conf/test.config | 34 +- docs/images/nf-core-nanoseq_logo.png | Bin 35189 -> 0 bytes docs/images/nf-core-nanoseq_logo_dark.png | 9 + docs/images/nf-core-nanoseq_logo_light.png | 13 + docs/output.md | 7 +- docs/usage.md | 36 -- modules/local/bam_rename.nf | 12 +- modules/local/bambu.nf | 17 +- modules/local/bedtools_bamtobed.nf | 18 +- modules/local/bedtools_genomecov.nf | 18 +- modules/local/deseq2.nf | 20 +- modules/local/dexseq.nf | 21 +- modules/local/functions.nf | 75 ---- modules/local/get_chrom_sizes.nf | 23 +- modules/local/get_nanolyse_fasta.nf | 6 - modules/local/get_software_versions.nf | 33 -- modules/local/get_test_data.nf | 6 - modules/local/graphmap2_align.nf | 21 +- modules/local/graphmap2_index.nf | 21 +- modules/local/gtf2bed.nf | 21 +- modules/local/guppy.nf | 13 +- modules/local/medaka_variant.nf | 22 +- modules/local/minimap2_align.nf | 21 +- modules/local/minimap2_index.nf | 21 +- modules/local/multiqc.nf | 30 +- modules/local/nanoplot.nf | 41 ++ modules/local/qcat.nf | 21 +- modules/local/samplesheet_check.nf | 24 +- modules/local/samtools_sort_index.nf | 26 +- modules/local/samtools_view_bam.nf | 21 +- modules/local/sniffles.nf | 21 +- modules/local/stringtie2.nf | 21 +- modules/local/subread_featurecounts.nf | 21 +- modules/local/ucsc_bed12tobigbed.nf | 24 +- modules/local/ucsc_bedgraphtobigwig.nf | 24 +- .../custom/dumpsoftwareversions/functions.nf | 78 ---- .../custom/dumpsoftwareversions/main.nf | 95 +---- .../custom/dumpsoftwareversions/meta.yml | 1 + .../templates/dumpsoftwareversions.py | 89 ++++ modules/nf-core/modules/fastqc/functions.nf | 68 --- modules/nf-core/modules/fastqc/main.nf | 39 +- modules/nf-core/modules/fastqc/meta.yml | 7 +- modules/nf-core/modules/multiqc/functions.nf | 68 --- modules/nf-core/modules/multiqc/main.nf | 35 -- modules/nf-core/modules/multiqc/meta.yml | 39 -- modules/nf-core/modules/nanolyse/functions.nf | 78 ---- modules/nf-core/modules/nanolyse/main.nf | 26 +- modules/nf-core/modules/nanoplot/functions.nf | 78 ---- modules/nf-core/modules/nanoplot/main.nf | 51 --- modules/nf-core/modules/nanoplot/meta.yml | 59 --- modules/nf-core/modules/pycoqc/functions.nf | 78 ---- modules/nf-core/modules/pycoqc/main.nf | 26 +- .../modules/samtools/flagstat/functions.nf | 78 ---- .../nf-core/modules/samtools/flagstat/main.nf | 27 +- .../modules/samtools/flagstat/meta.yml | 24 +- .../modules/samtools/idxstats/functions.nf | 79 ---- .../nf-core/modules/samtools/idxstats/main.nf | 25 +- .../modules/samtools/idxstats/meta.yml | 24 +- .../modules/samtools/index/functions.nf | 78 ---- .../nf-core/modules/samtools/index/main.nf | 36 +- .../nf-core/modules/samtools/index/meta.yml | 33 +- .../modules/samtools/sort/functions.nf | 78 ---- modules/nf-core/modules/samtools/sort/main.nf | 29 +- .../nf-core/modules/samtools/sort/meta.yml | 24 +- .../modules/samtools/stats/functions.nf | 78 ---- .../nf-core/modules/samtools/stats/main.nf | 31 +- .../nf-core/modules/samtools/stats/meta.yml | 45 +- .../modules/stringtie/merge/functions.nf | 78 ---- .../nf-core/modules/stringtie/merge/main.nf | 24 +- nextflow.config | 41 +- subworkflows/local/align_graphmap2.nf | 8 +- subworkflows/local/align_minimap2.nf | 8 +- subworkflows/local/bam_sort_index_samtools.nf | 12 +- subworkflows/local/bedtools_ucsc_bigbed.nf | 6 +- subworkflows/local/bedtools_ucsc_bigwig.nf | 6 +- .../local/differential_deseq2_dexseq.nf | 8 +- subworkflows/local/dna_variant_calling.nf | 10 +- subworkflows/local/input_check.nf | 5 +- subworkflows/local/prepare_genome.nf | 6 +- .../qcbasecall_pycoqc_nanoplot.nf | 7 +- .../qcfastq_nanoplot_fastqc.nf | 7 +- .../local/quantify_stringtie_featurecounts.nf | 9 +- subworkflows/local/sv_sniffles.nf | 24 -- subworkflows/local/vc_medaka.nf | 42 -- subworkflows/nf-core/bam_stats_samtools.nf | 8 +- workflows/nanoseq.nf | 77 ++-- 105 files changed, 960 insertions(+), 2598 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 assets/nf-core-nanoseq_logo.png create mode 100644 assets/nf-core-nanoseq_logo_light.png delete mode 100755 bin/markdown_to_html.py delete mode 100755 bin/scrape_software_versions.py delete mode 100644 docs/images/nf-core-nanoseq_logo.png create mode 100644 docs/images/nf-core-nanoseq_logo_dark.png create mode 100644 docs/images/nf-core-nanoseq_logo_light.png delete mode 100644 modules/local/functions.nf delete mode 100644 modules/local/get_software_versions.nf create mode 100644 modules/local/nanoplot.nf delete mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py delete mode 100644 modules/nf-core/modules/fastqc/functions.nf delete mode 100644 modules/nf-core/modules/multiqc/functions.nf delete mode 100644 modules/nf-core/modules/multiqc/main.nf delete mode 100644 modules/nf-core/modules/multiqc/meta.yml delete mode 100644 modules/nf-core/modules/nanolyse/functions.nf delete mode 100644 modules/nf-core/modules/nanoplot/functions.nf delete mode 100644 modules/nf-core/modules/nanoplot/main.nf delete mode 100644 modules/nf-core/modules/nanoplot/meta.yml delete mode 100644 modules/nf-core/modules/pycoqc/functions.nf delete mode 100644 modules/nf-core/modules/samtools/flagstat/functions.nf delete mode 100644 modules/nf-core/modules/samtools/idxstats/functions.nf delete mode 100644 modules/nf-core/modules/samtools/index/functions.nf delete mode 100644 modules/nf-core/modules/samtools/sort/functions.nf delete mode 100644 modules/nf-core/modules/samtools/stats/functions.nf delete mode 100644 modules/nf-core/modules/stringtie/merge/functions.nf rename subworkflows/{nf-core => local}/qcbasecall_pycoqc_nanoplot.nf (83%) rename subworkflows/{nf-core => local}/qcfastq_nanoplot_fastqc.nf (86%) delete mode 100644 subworkflows/local/sv_sniffles.nf delete mode 100644 subworkflows/local/vc_medaka.nf diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e102ae29..cdbebfc6 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -68,16 +68,13 @@ If you wish to contribute a new step, please use the following coding standards: 1. Define the corresponding input channel into your new process from the expected previous process channel 2. Write the process block (see below). 3. Define the output channel if needed (see below). -4. Add any new flags/options to `nextflow.config` with a default (see below). -5. Add any new flags/options to `nextflow_schema.json` with help text (with `nf-core schema build`). -6. Add any new flags/options to the help message (for integer/text parameters, print to help the corresponding `nextflow.config` parameter). -7. Add sanity checks for all relevant parameters. -8. Add any new software to the `scrape_software_versions.py` script in `bin/` and the version command to the `scrape_software_versions` process in `main.nf`. -9. Do local tests that the new code works properly and as expected. -10. Add a new test command in `.github/workflow/ci.yml`. -11. If applicable add a [MultiQC](https://https://multiqc.info/) module. -12. Update MultiQC config `assets/multiqc_config.yaml` so relevant suffixes, name clean up, General Statistics Table column order, and module figures are in the right order. -13. Optional: Add any descriptions of MultiQC report sections and output files to `docs/output.md`. +4. Add any new parameters to `nextflow.config` with a default (see below). +5. Add any new parameters to `nextflow_schema.json` with help text (via the `nf-core schema build` tool). +6. Add sanity checks and validation for all relevant parameters. +7. Perform local tests to validate that the new code works as expected. +8. If applicable, add a new test command in `.github/workflow/ci.yml`. +9. Update MultiQC config `assets/multiqc_config.yaml` so relevant suffixes, file name clean up and module plots are in the appropriate order. If applicable, add a [MultiQC](https://https://multiqc.info/) module. +10. Add a description of the output files and if relevant any appropriate images from the MultiQC report to `docs/output.md`. ### Default values @@ -102,27 +99,6 @@ Please use the following naming schemes, to make it easy to understand what is g If you are using a new feature from core Nextflow, you may bump the minimum required version of nextflow in the pipeline with: `nf-core bump-version --nextflow . [min-nf-version]` -### Software version reporting - -If you add a new tool to the pipeline, please ensure you add the information of the tool to the `get_software_version` process. - -Add to the script block of the process, something like the following: - -```bash - --version &> v_.txt 2>&1 || true -``` - -or - -```bash - --help | head -n 1 &> v_.txt 2>&1 || true -``` - -You then need to edit the script `bin/scrape_software_versions.py` to: - -1. Add a Python regex for your tool's `--version` output (as in stored in the `v_.txt` file), to ensure the version is reported as a `v` and the version number e.g. `v2.1.1` -2. Add a HTML entry to the `OrderedDict` for formatting in MultiQC. - ### Images and figures For overview images and other documents we follow the nf-core [style guidelines and examples](https://nf-co.re/developers/design_guidelines). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 21c75a55..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -name: Bug report -about: Report something that is broken or incorrect -labels: bug ---- - - - -## Check Documentation - -I have checked the following places for your error: - -- [ ] [nf-core website: troubleshooting](https://nf-co.re/usage/troubleshooting) -- [ ] [nf-core/nanoseq pipeline documentation](https://nf-co.re/nanoseq/usage) - -## Description of the bug - - - -## Steps to reproduce - -Steps to reproduce the behaviour: - -1. Command line: -2. See error: - -## Expected behaviour - - - -## Log files - -Have you provided the following extra information/files: - -- [ ] The command used to run the pipeline -- [ ] The `.nextflow.log` file - -## System - -- Hardware: -- Executor: -- OS: -- Version - -## Nextflow Installation - -- Version: - -## Container engine - -- Engine: -- version: - -## Additional context - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..55e0a8a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,52 @@ + +name: Bug report +description: Report something that is broken or incorrect +labels: bug +body: + + - type: markdown + attributes: + value: | + Before you post this issue, please check the documentation: + + - [nf-core website: troubleshooting](https://nf-co.re/usage/troubleshooting) + - [nf-core/nanoseq pipeline documentation](https://nf-co.re/nanoseq/usage) + + - type: textarea + id: description + attributes: + label: Description of the bug + description: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + id: command_used + attributes: + label: Command used and terminal output + description: Steps to reproduce the behaviour. Please paste the command you used to launch the pipeline and the output from your terminal. + render: console + placeholder: | + $ nextflow run ... + + Some output where something broke + + - type: textarea + id: files + attributes: + label: Relevant files + description: | + Please drag and drop the relevant files here. Create a `.zip` archive if the extension is not allowed. + Your verbose log file `.nextflow.log` is often useful _(this is a hidden file in the directory where you launched the pipeline)_ as well as custom Nextflow configuration files. + + - type: textarea + id: system + attributes: + label: System information + description: | + * Nextflow version _(eg. 21.10.3)_ + * Hardware _(eg. HPC, Desktop, Cloud)_ + * Executor _(eg. slurm, local, awsbatch)_ + * Container engine: _(e.g. Docker, Singularity, Conda, Podman, Shifter or Charliecloud)_ + * OS _(eg. CentOS Linux, macOS, Linux Mint)_ + * Version of nf-core/nanoseq _(eg. 1.1, 1.5, 1.8.2)_ diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 904ded2b..74fd1b53 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,3 @@ -blank_issues_enabled: false contact_links: - name: Join nf-core url: https://nf-co.re/join diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index e7d6e675..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for the nf-core/nanoseq pipeline -labels: enhancement ---- - - - -## Is your feature request related to a problem? Please describe - - - - - -## Describe the solution you'd like - - - -## Describe alternatives you've considered - - - -## Additional context - - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..c75fe931 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,11 @@ +name: Feature request +description: Suggest an idea for the nf-core/nanoseq pipeline +labels: enhancement +body: + - type: textarea + id: description + attributes: + label: Description of feature + description: Please describe your suggestion for a new feature. It might help to describe a problem or use case, plus any alternatives that you have considered. + validations: + required: true diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 164c35de..c306f9d2 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -14,14 +14,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Launch workflow via tower - uses: nf-core/tower-action@master + uses: nf-core/tower-action@v2 # TODO nf-core: You can customise AWS full pipeline tests as required # Add full size test data (but still relatively small datasets for few samples) # on the `test_full.config` test runs with only one set of parameters with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - bearer_token: ${{ secrets.TOWER_BEARER_TOKEN }} + access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} pipeline: ${{ github.repository }} revision: ${{ github.sha }} @@ -30,4 +30,5 @@ jobs: { "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/nanoseq/results-${{ github.sha }}" } - profiles: '[ "test_full", "aws_tower" ]' + profiles: test_full,aws_tower + pre_run_script: 'export NXF_VER=21.10.3' diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml index 5253f88f..d3f7acac 100644 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -11,19 +11,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Launch workflow via tower - uses: nf-core/tower-action@master - + uses: nf-core/tower-action@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - bearer_token: ${{ secrets.TOWER_BEARER_TOKEN }} + access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} pipeline: ${{ github.repository }} revision: ${{ github.sha }} workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/nanoseq/work-${{ github.sha }} parameters: | { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/nanoseq/results-${{ github.sha }}" + "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/nanoseq/results-test-${{ github.sha }}" } - - profiles: '[ "test", "aws_tower" ]' - + profiles: test,aws_tower + pre_run_script: 'export NXF_VER=21.10.3' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d21b0f19..f7ad3b29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,8 +8,9 @@ on: release: types: [published] -# Uncomment if we need an edge release of Nextflow again -# env: NXF_EDGE: 1 +env: + NXF_ANSI_LOG: false + CAPSULE_LOG: none jobs: test: @@ -17,20 +18,26 @@ jobs: # Only run on push if this is the nf-core dev branch (merged PRs) if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/nanoseq') }} runs-on: ubuntu-latest - env: - NXF_VER: ${{ matrix.nxf_ver }} - NXF_ANSI_LOG: false strategy: matrix: - # Nextflow versions: check pipeline minimum and current latest - nxf_ver: ['21.04.0', ''] + # Nextflow versions + include: + # Test pipeline minimum Nextflow version + - NXF_VER: '21.10.3' + NXF_EDGE: '' + # Test latest edge release of Nextflow + - NXF_VER: '' + NXF_EDGE: '1' steps: - name: Check out pipeline code uses: actions/checkout@v2 - name: Install Nextflow env: - CAPSULE_LOG: none + NXF_VER: ${{ matrix.NXF_VER }} + # Uncomment only if the edge release is more recent than the latest stable release + # See https://github.com/nextflow-io/nextflow/issues/2467 + # NXF_EDGE: ${{ matrix.NXF_EDGE }} run: | wget -qO- get.nextflow.io | bash sudo mv nextflow /usr/local/bin/ @@ -44,7 +51,7 @@ jobs: if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/nanoseq') }} runs-on: ubuntu-latest env: - NXF_VER: '21.04.0' + NXF_VER: '21.10.3' NXF_ANSI_LOG: false strategy: matrix: @@ -69,7 +76,7 @@ jobs: if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/nanoseq') }} runs-on: ubuntu-latest env: - NXF_VER: '21.04.0' + NXF_VER: '21.10.3' NXF_ANSI_LOG: false strategy: matrix: @@ -94,7 +101,7 @@ jobs: if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/nanoseq') }} runs-on: ubuntu-latest env: - NXF_VER: '21.04.0' + NXF_VER: '21.10.3' NXF_ANSI_LOG: false strategy: matrix: diff --git a/.github/workflows/linting_comment.yml b/.github/workflows/linting_comment.yml index 90f03c6f..44d72994 100644 --- a/.github/workflows/linting_comment.yml +++ b/.github/workflows/linting_comment.yml @@ -15,6 +15,7 @@ jobs: uses: dawidd6/action-download-artifact@v2 with: workflow: linting.yml + workflow_conclusion: completed - name: Get PR number id: pr_number diff --git a/README.md b/README.md index 44666d41..0de86232 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -# ![nfcore/nanoseq](docs/images/nf-core-nanoseq_logo.png) +# ![nf-core/nanoseq](docs/images/nf-core-nanoseq_logo_light.png#gh-light-mode-only) ![nf-core/nanoseq](docs/images/nf-core-nanoseq_logo_dark.png#gh-dark-mode-only) [![GitHub Actions CI Status](https://github.com/nf-core/nanoseq/workflows/nf-core%20CI/badge.svg)](https://github.com/nf-core/nanoseq/actions?query=workflow%3A%22nf-core+CI%22) [![GitHub Actions Linting Status](https://github.com/nf-core/nanoseq/workflows/nf-core%20linting/badge.svg)](https://github.com/nf-core/nanoseq/actions?query=workflow%3A%22nf-core+linting%22) [![AWS CI](https://img.shields.io/badge/CI%20tests-Full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/nanoseq/results) [![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.3697959-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.3697959) -[![Nextflow](https://img.shields.io/badge/nextflow-%E2%89%A521.04.0-23aa62.svg?labelColor=000000)](https://www.nextflow.io/) +[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A521.10.3-23aa62.svg?labelColor=000000)](https://www.nextflow.io/) +[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) [![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/) [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) [![Docker](https://img.shields.io/docker/automated/nfcore/nanoseq.svg)](https://hub.docker.com/r/nfcore/nanoseq) @@ -49,18 +50,21 @@ On release, automated continuous integration tests run the pipeline on a [full-s ## Quick Start -1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=21.04.0`) +1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=21.10.3`) 2. Install any of [`Docker`](https://docs.docker.com/engine/installation/), [`Singularity`](https://www.sylabs.io/guides/3.0/user-guide/), [`Podman`](https://podman.io/), [`Shifter`](https://nersc.gitlab.io/development/shifter/how-to-use/) or [`Charliecloud`](https://hpc.github.io/charliecloud/) for full pipeline reproducibility _(please only use [`Conda`](https://conda.io/miniconda.html) as a last resort; see [docs](https://nf-co.re/usage/configuration#basic-configuration-profiles))_ 3. Download the pipeline and test it on a minimal dataset with a single command: ```console - nextflow run nf-core/nanoseq -profile test, + nextflow run nf-core/nanoseq -profile test,YOURPROFILE ``` + Note that some form of configuration will be needed so that Nextflow knows how to fetch the required software. This is usually done in the form of a config profile (`YOURPROFILE` in the example command above). You can chain multiple config profiles in a comma-separated string. + + > * The pipeline comes with config profiles called `docker`, `singularity`, `podman`, `shifter`, `charliecloud` and `conda` which instruct the pipeline to use the named tool for software management. For example, `-profile test,docker`. > * Please check [nf-core/configs](https://github.com/nf-core/configs#documentation) to see if a custom config file to run nf-core pipelines already exists for your Institute. If so, you can simply use `-profile ` in your command. This will enable either `docker` or `singularity` and set the appropriate execution settings for your local compute environment. - > * If you are using `singularity` then the pipeline will auto-detect this and attempt to download the Singularity images directly as opposed to performing a conversion from Docker images. If you are persistently observing issues downloading Singularity images directly due to timeout or network issues then please use the `--singularity_pull_docker_container` parameter to pull and convert the Docker image instead. Alternatively, it is highly recommended to use the [`nf-core download`](https://nf-co.re/tools/#downloading-pipelines-for-offline-use) command to pre-download all of the required containers before running the pipeline and to set the [`NXF_SINGULARITY_CACHEDIR` or `singularity.cacheDir`](https://www.nextflow.io/docs/latest/singularity.html?#singularity-docker-hub) Nextflow options to be able to store and re-use the images from a central location for future pipeline runs. + > * If you are using `singularity` and are persistently observing issues downloading Singularity images directly due to timeout or network issues, then you can use the `--singularity_pull_docker_container` parameter to pull and convert the Docker image instead. Alternatively, you can use the [`nf-core download`](https://nf-co.re/tools/#downloading-pipelines-for-offline-use) command to download images first, before running the pipeline. Setting the [`NXF_SINGULARITY_CACHEDIR` or `singularity.cacheDir`](https://www.nextflow.io/docs/latest/singularity.html?#singularity-docker-hub) Nextflow options enables you to store and re-use the images from a central location for future pipeline runs. > * If you are using `conda`, it is highly recommended to use the [`NXF_CONDA_CACHEDIR` or `conda.cacheDir`](https://www.nextflow.io/docs/latest/conda.html) settings to store the environments in a central location for future pipeline runs. 4. Start running your own analysis! diff --git a/assets/multiqc_config.yaml b/assets/multiqc_config.yaml index 35b33cc7..365f1651 100644 --- a/assets/multiqc_config.yaml +++ b/assets/multiqc_config.yaml @@ -1,7 +1,7 @@ report_comment: > This report has been generated by the nf-core/nanoseq analysis pipeline. For information about how to interpret these results, please see the - documentation. + documentation. report_section_order: software_versions: order: -1000 diff --git a/assets/nf-core-nanoseq_logo.png b/assets/nf-core-nanoseq_logo.png deleted file mode 100644 index d6bad792a70273b98164676f0c7f44fb5b9930fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18337 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 Jl}H%+{Xe$uM)Cjv diff --git a/assets/nf-core-nanoseq_logo_light.png b/assets/nf-core-nanoseq_logo_light.png new file mode 100644 index 00000000..d46347c2 --- /dev/null +++ b/assets/nf-core-nanoseq_logo_light.png @@ -0,0 +1,11 @@ + + +503 Service Unavailable + +

Service Unavailable

+

The server is temporarily unable to service your +request due to maintenance downtime or capacity +problems. Please try again later.

+

Additionally, a 503 Service Unavailable +error was encountered while trying to use an ErrorDocument to handle the request.

+ diff --git a/assets/sendmail_template.txt b/assets/sendmail_template.txt index c13fd9d4..5047c306 100644 --- a/assets/sendmail_template.txt +++ b/assets/sendmail_template.txt @@ -12,9 +12,9 @@ $email_html Content-Type: image/png;name="nf-core-nanoseq_logo.png" Content-Transfer-Encoding: base64 Content-ID: -Content-Disposition: inline; filename="nf-core-nanoseq_logo.png" +Content-Disposition: inline; filename="nf-core-nanoseq_logo_light.png" -<% out << new File("$projectDir/assets/nf-core-nanoseq_logo.png"). +<% out << new File("$projectDir/assets/nf-core-nanoseq_logo_light.png"). bytes. encodeBase64(). toString(). diff --git a/bin/markdown_to_html.py b/bin/markdown_to_html.py deleted file mode 100755 index fedf1097..00000000 --- a/bin/markdown_to_html.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function -import argparse -import markdown -import os -import sys -import io - - -def convert_markdown(in_fn): - input_md = io.open(in_fn, mode="r", encoding="utf-8").read() - html = markdown.markdown( - "[TOC]\n" + input_md, - extensions=["pymdownx.extra", "pymdownx.b64", "pymdownx.highlight", "pymdownx.emoji", "pymdownx.tilde", "toc"], - extension_configs={ - "pymdownx.b64": {"base_path": os.path.dirname(in_fn)}, - "pymdownx.highlight": {"noclasses": True}, - "toc": {"title": "Table of Contents"}, - }, - ) - return html - - -def wrap_html(contents): - header = """ - - - - - -
- """ - footer = """ -
- - - """ - return header + contents + footer - - -def parse_args(args=None): - parser = argparse.ArgumentParser() - parser.add_argument("mdfile", type=argparse.FileType("r"), nargs="?", help="File to convert. Defaults to stdin.") - parser.add_argument( - "-o", "--out", type=argparse.FileType("w"), default=sys.stdout, help="Output file name. Defaults to stdout." - ) - return parser.parse_args(args) - - -def main(args=None): - args = parse_args(args) - converted_md = convert_markdown(args.mdfile.name) - html = wrap_html(converted_md) - args.out.write(html) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/bin/scrape_software_versions.py b/bin/scrape_software_versions.py deleted file mode 100755 index ffabc860..00000000 --- a/bin/scrape_software_versions.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function -import os - -results = {} -version_files = [x for x in os.listdir(".") if x.endswith(".version.txt")] -for version_file in version_files: - - software = version_file.replace(".version.txt", "") - if software == "pipeline": - software = "nf-core/nanoseq" - - with open(version_file) as fin: - version = fin.read().strip() - results[software] = version - -# Dump to YAML -print( - """ -id: 'software_versions' -section_name: 'nf-core/nanoseq Software Versions' -section_href: 'https://github.com/nf-core/nanoseq' -plot_type: 'html' -description: 'are collected at run time from the software output.' -data: | -
-""" -) -for k, v in sorted(results.items()): - print("
{}
{}
".format(k, v)) -print("
") - -# Write out as tsv file: -with open("software_versions.tsv", "w") as f: - for k, v in sorted(results.items()): - f.write("{}\t{}\n".format(k, v)) diff --git a/conf/base.config b/conf/base.config index e4510c84..754c711a 100644 --- a/conf/base.config +++ b/conf/base.config @@ -52,4 +52,7 @@ process { errorStrategy = 'retry' maxRetries = 2 } + withName:CUSTOM_DUMPSOFTWAREVERSIONS { + cache = false + } } diff --git a/conf/modules.config b/conf/modules.config index ab9531fd..e969133a 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -1,117 +1,332 @@ /* - * -------------------------------------------------- - * Config file for defining DSL2 per module options - * -------------------------------------------------- - * - * Available keys to override module options: - * args = Additional arguments appended to command in module. - * args2 = Second set of arguments appended to command in module (multi-tool modules). - * publish_dir = Directory to publish results. - * publish_by_id = Publish results in separate folders by meta.id value. - * publish_files = Groovy map where key = "file_ext" and value = "directory" to publish results for that file extension - * The value of "directory" is appended to the standard "publish_dir" path as defined above. - * If publish_files == null (unspecified) - All files are published. - * If publish_files == false - No files are published. - * suffix = File name suffix for output files. - * - */ +======================================================================================== + Config file for defining DSL2 per module options and publishing paths +======================================================================================== + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ -params { - modules { - 'get_test_data'{ - publish_dir = 'test-datasets' - } - 'check_samplesheet' { - publish_dir = 'public_data' - } - 'guppy' { - publish_dir = 'guppy' - } - 'qcat' { - publish_dir = 'qcat' - } - 'pycoqc' { - publish_dir = 'pycoqc' - } - 'nanolyse' { - publish_dir = 'nanolyse' - publish_files = ['.log':'log', '.gz': '.'] - } - 'nanoplot' { - publish_dir = 'nanoplot' - } - 'fastqc' { - publish_dir = 'fastqc' +process { + + publishDir = [ + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + + // nanoseq.nf includes + withName: CUSTOM_DUMPSOFTWAREVERSIONS { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: 'copy', + pattern: '*_versions.yml' + ] + } + + // INPUT_CHECK + withName: 'NFCORE_NANOSEQ:NANOSEQ:INPUT_CHECK:SAMPLESHEET_CHECK' { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } +} + +if (!params.skip_basecalling) { + process { + withName: GUPPY { + publishDir = [ + path: { "${params.outdir}/guppy" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } - 'get_chrom_sizes' { - publish_dir = "${params.aligner}" - publish_files = ['sizes':'genome'] + } +} + +if (params.skip_basecalling && !params.skip_demultiplexing) { + process { + withName: QCAT { + publishDir = [ + path: { "${params.outdir}/qcat" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } - 'gtf_to_bed' { - publish_dir = "${params.aligner}" + } +} + +if (params.run_nanolyse) { + process { + withName: NANOLYSE { + publishDir = [ + path: { "${params.outdir}/nanolyse" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } - 'minimap2_index' { - publish_dir = "${params.aligner}" - publish_files = ['.mmi':'genome'] + } +} + +if (!params.skip_qc) { + if (!params.skip_basecalling && !params.skip_pycoqc) { + process { + withName: PYCOQC { + publishDir = [ + path: { "${params.outdir}/pycoqc" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - 'graphmap2_index' { - publish_dir = "${params.aligner}" - publish_files = ['.gmidx':'genome'] + } + if (!params.skip_nanoplot) { + process { + withName: NANOPLOT { + publishDir = [ + path: { "${params.outdir}/nanoplot" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - 'minimap2_align' { - publish_files = false + } + if (!params.skip_fastqc) { + process { + withName: FASTQC { + publishDir = [ + path: { "${params.outdir}/fastqc" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - 'graphmap2_align' { - publish_files = false + } +} + +if (!params.skip_alignment) { + process { + withName: GTF2BED { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } - 'samtools_sort' { - suffix = '.sorted' - publish_files = ['stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats', 'bam':'bam', 'bai':'bam'] - publish_dir = "${params.aligner}" + withName: GET_CHROM_SIZES { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } - 'medaka_vc' { - publish_dir = "${params.aligner}" - publish_files = ['.vcf':'medaka'] - args = '-s r941_prom_hac_snp_g507 -m r941_prom_hac_variant_g507' + } + if (params.aligner == "graphmap2") { + process { + withName: SAMTOOLS_SORT { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_INDEX { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: BAM_STATS_SAMTOOLS { + publishDir = [ + path: { "${params.outdir}/graphmap2/samtools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - 'sniffles_sv' { - publish_dir = "${params.aligner}" - publish_files = ['.vcf':'sniffles'] + if (!params.skip_bigbed) { + process { + withName: BEDTOOLS_UCSC_BIGBED { + publishDir = [ + path: { "${params.outdir}/graphmap2/bigbed" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } } - 'bedtools_genomecov' { - publish_files = false + if (!params.skip_bigwig) { + process { + withName: BEDTOOLS_UCSC_BIGWIG { + publishDir = [ + path: { "${params.outdir}/graphmap2/bigwig" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } } - 'ucsc_bedgraphtobigwig' { - publish_dir = "${params.aligner}" - publish_files = ['.bigWig':'bigWig'] + } + if (params.aligner == "minimap2") { + process { + withName: SAMTOOLS_SORT { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_INDEX { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: BAM_STATS_SAMTOOLS { + publishDir = [ + path: { "${params.outdir}/minimap2/samtools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - 'bedtools_bamtobed' { - publish_files = false + if (!params.skip_bigbed) { + process { + withName: BEDTOOLS_UCSC_BIGBED { + publishDir = [ + path: { "${params.outdir}/minimap2/bigbed" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } } - 'ucsc_bed12tobigbed' { - publish_dir = "${params.aligner}" - publish_files = ['.bigBed':'bigBed'] + if (!params.skip_bigwig) { + process { + withName: BEDTOOLS_UCSC_BIGWIG { + publishDir = [ + path: { "${params.outdir}/minimap2/bigwig" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } } - 'stringtie2' { - publish_dir = "stringtie2" + } +} + +if (params.call_variants) { + if (!params.skip_medaka) { + process { + withName: MEDAKA_VARIANT { + publishDir = [ + path: { "${params.outdir}/DNA_variant_calling/medaka_variant" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - 'stringtie2_merge' { - publish_dir = "stringtie2" + } + if (!params.skip_sniffles) { + process { + withName: SNIFFLES { + publishDir = [ + path: { "${params.outdir}/DNA_variant_calling/sniffles" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - 'subread_featurecounts' { - publish_dir = "stringtie2/featurecounts" + } +} + +if (!params.skip_quantification) { + if (params.quantification_method == "bambu") { + process { + withName: BAMBU { + publishDir = [ + path: { "${params.outdir}/bambu" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - 'bambu'{ - publish_dir = "bambu" + if (!params.differential_analysis) { + process { + withName: DESEQ2 { + publishDir = [ + path: { "${params.outdir}/bambu/deseq2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: DEXSEQ { + publishDir = [ + path: { "${params.outdir}/bambu/dexseq" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } } - 'deseq2' { - publish_dir = "differential_analysis/deseq2" + } + if (params.quantification_method == "stringtie2") { + process { + withName: STRINGTIE2 { + publishDir = [ + path: { "${params.outdir}/stringtie2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SUBREAD_FEATURECOUNTS { + publishDir = [ + path: { "${params.outdir}/stringtie2/featureCounts" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - 'dexseq' { - publish_dir = "differential_analysis/dexseq" + if (!params.differential_analysis) { + process { + withName: DESEQ2 { + publishDir = [ + path: { "${params.outdir}/bambu/deseq2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: DEXSEQ { + publishDir = [ + path: { "${params.outdir}/bambu/dexseq" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } } - 'multiqc' { - publish_dir = "multiqc" + } +} + +if (!params.skip_multiqc) { + process { + withName: MULTIQC { + ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' + publishDir = [ + path: { [ + "${params.outdir}/multiqc", + params.skip_alignment? '' : "/${params.aligner}" + ].join('') }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } } } diff --git a/conf/test.config b/conf/test.config index b4b12b00..3a3eecba 100644 --- a/conf/test.config +++ b/conf/test.config @@ -11,24 +11,24 @@ */ params { - config_profile_name = 'Test profile' - config_profile_description = 'Minimal test dataset to check pipeline function' + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset to check pipeline function' - // Limit resources so that this can run on GitHub Actions - max_cpus = 2 - max_memory = 6.GB - max_time = 12.h + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' - // Input data to perform both basecalling and demultiplexing - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/nanoseq/samplesheet_bc_dx.csv' - protocol = 'cDNA' - flowcell = 'FLO-MIN106' - kit = 'SQK-DCS109' - barcode_kit = 'EXP-NBD103' - run_nanolyse = true - skip_quantification = true + // Input data to perform both basecalling and demultiplexing + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/nanoseq/samplesheet_bc_dx.csv' + protocol = 'cDNA' + flowcell = 'FLO-MIN106' + kit = 'SQK-DCS109' + barcode_kit = 'EXP-NBD103' + run_nanolyse = true + skip_quantification = true - // This variable is just for reference and isnt actually required for the tests - // Files are downloaded and staged using the "GetTestData" process - input_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/nanoseq/fast5/barcoded/' + // This variable is just for reference and isnt actually required for the tests + // Files are downloaded and staged using the "GetTestData" process + input_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/nanoseq/fast5/barcoded/' } diff --git a/docs/images/nf-core-nanoseq_logo.png b/docs/images/nf-core-nanoseq_logo.png deleted file mode 100644 index d71a24951fa9abcd9335b37a1198c9110023a7ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35189 zcmYhi1yodBxIa9A(lJPPiL@XcLze>5Eezcq!qBOdfRfTB-QCjNT}ls)bk}!y@4f%^ zorTM_#EG-dexB!7J4{(o77Lve9RvbleRwaW3IZW&gFpx`P?3RuIq#Gk0scaBe6QmI z0%3PQ|3Qdn!6pNNs6ij3#MNP`2R`np_|l6{^`4VkEvy0_IHIzhW^rZ_xUGj60}AGxu_i;ogA1=gxxLwDVXH~QwAz^eU7GC(otezOn|={hsBAh8O_pu- z$+EPjY)h&EO2}ly8L1E?;F7eRm!?_lvYyDMn8>W(Q2bgDPjDwhOEnT;?rnW)?IZO` z$?W;dDv6|D5FnO&g)2vM8GmMG)}dfS{@;skm2t+mUk~kFsqQbyf0d;1@q#t3T)z)t z|NHxgilHZE45uE_&{BR)Ii2)+SP73usdFx=13l@$W)urcxkl|{;K?@&$VR+g5$zU! zmTrC#oH*%=Zn_=4EQ?9e2_~joKLz`Li#FGl&m3c)^naiI^%DE7X5j2`+oqsYlIn#; z>#H6vj{mv3F(w7I2OW5}B}Y`kJ}X40Vk)zEF%J!T8ga%ZJ+GclH0#r>U^+Tv%_AN0 zpo2TpF5^K?Gbunkcu}(SbeoNM4A#p99Vm{ZFYJ`Z*8D_A!W=xXm@mOugR>DEV)6Wh zLb0R3h$l$FUfI@Fnw|pXkg||O$&rNF)!GL24U;syZ%TGh-*5kOYc2N;_p#RA^Phs_ zWDJPp&}z@kGvk$PP8Fi!NQnQthG`CHt}H1FUQHkymok0PR>O+3cHm62Odq&i3^^$r z`R{pGExda@V@Sjzb^k_crCLsRSBm(snDQY|=bflqfD<{4>=WI~$LmpnjiQQ1K zfW90<8gRSb)@Oqzx>kY^rI%9t;?9g|bUXr-<2j-#5dWWM3Oq>x&sPU#j1i`dO_JT^ zaQpvUV2kcQ2RiIE5ILZi9XWh^67^NAesk%2~ro7Vq|GmcW zB`$VCp~5R8c!h{J|bGuS62GT>3S7mT3w@K z!!1Y^J!RzUw-Q!YW+w4+--mau*N)$axd^}X^r&3W-{NMNZZ4RQp<6D*S^rN#Tl=eq z8*+p?h&++YuXtcI`#q9C#)i~DaUiK%1Zpxqa%_~cBxk%W$T^2CZ%Uj6=ksomE!iwP z@0L1ZO->|a@24}h8(l53@8&eb#SE!cd68beY%l z<2j7TheSuOcoAV__$mHGlwy2O-+V635Uxt1gaD?!Hd045*LPij_m2+5`{wi1{d(n) z$N8q=v);j#|x@XYWc4p+u$$zQE&zdiGPG z9!bS3#`YHu$T9j6MQ8w=rK`q(~G>}G$!MOFbCoi_)#=V(x zD)Xl`QKx_XKPrUC!QxEPlKK?O`Ynw3y@=iKHSM7yc_ZnOHukBA&0REb_DeGB2U)oj zb(@-6P@{yfH)lt|Aw~RuJ!J=$8D~6owX&vkwSv+%h5~9ZJoo}T-?jLjmQpN8IXySK zo~3h@>Dq@464P|!Ela((`QyEv#<-{6*$c+17)jTFy{V4)0u)#lU3Ot5uDkw|O)A6* z5yWA7?FixWqv5Qs;`Wnd`qH=25xr7^1oueg^Tc%KPJQ>45O_7vi(JJ$Tz<8DFb38# z;}0Ykzr5ubvg~`P;g2YApjU{$J`5D~EXfF&Uk|~*#OdL%-KjxL5&jVJ`h6}_L_p~mCO8zBOH&49S z+27GZzLa(*itGoI-5paWEIv1mLx83 zysw>m&;yVh2fB;DC#9uvw8|-ZuoA!&5B)VP1r#dDndD~pB5mz{)Af6bOq{9&O}LqP z_iX^(z%(YBTp%!7R>+*o3tn|=9^BZ~knp9=mCNS{|3B-A5#pnvpoR1G; zl@&z&GKh?d7-Pg?;q82KDnh+rYXX!kWNf2$4m$O4Khxl}!Qr|yu368ebB(p)i;=!9 zj)0#Sm$}j&U?d(7+t-LXvKEn)-9F5wmaX}ou~-QU#)nhDIFAp-^Wp;Y@imq`y|o(2 zQi?8(EDmwW9m=F~R**VzmD3T|(ZhZ{$ei0cu+ADoyr3@u&j{Y*V6-u&aW;ia$ zPCRx!yNS?#=(aJVWiSIN8@3ot6;O4|tUXX5OjivrCF4Ww1EqS*a`2l*g!O(Z=j6aE z_g*4%(0ZdRNG_AvGBm>?8xj0Y=fR%(y+=toYfp@%O~vnQ{NI_P_fdhW#~t61$QKjv zATJTVBp&lmI-<*xDz2qW2{}p+2skb{Yp|nN+YN3;F>!Ic#t^w)MM@R?ET^^j_;6>k z-W|r|Maefkx6vPfl!;IMVn@_cSz@17hdi}zOM}l#6XSsi1;$2iY*DgBZh3;)d#pCX zel5EzJFj{X0Y1gjph5WcaU6|?%`f=y57RL>tB3u}>n+Wua(}CAE@%3yTO5-7Se!Bh_`EK%RUNQCf2sz%reAzw$?`~Qd zG*HNd)ZP(l8GMjW(u#^f4J8UO^ylbbD{B9 zo=f2;2ZBYqN8T3oPxQ>V>?T#$ieS(2>OJA0&jr7wE!7je2F*?3nPpoqYTslioaPlh z!1h;3v&zIIe1B*=&@3QQ4FNC{*Ltqhdll$~cq4N&GixJ!E5Vdt380c~9mx~9FY908 zfFiN5XkLL3Apf?4_u}gV7GW{W-ra)Q=4rV=9Z7e^Mb0L-BvmoUVXIQIAKu|V8j5XU zOuXpdGqW&M4qD^^hAKlcihr(rVs=XlxhGJm%6iTN^td-~J~is*EVoeNfc(EgO7;c^ z$5%iF{fH2PbQ=tJN>oPp7ku<>CSYr{sfAgj2*+;zJ@AnIi+itLYfyo=-V@Nj6HEX5 zr8Q+^LRSv}Ws?RlHpjQU0eXW_mH8p!A9mw&zU))v50vzR0 zq_S1k8Mehd{UTX;!%i%%9?gCkMHDL#(a~SBY2-_14Y{k_ko46#?fKBZKwq6SmCZo} z@razRhWy#J#`s+d2fX>|E4oca;9gTnn!z@I;QVKz2*r3q<>$0xqzc17)%i(T_m zj^rVvO6%GtFZTjREKeKPu&%}Von(+f1W?_omrQy@TeNmw;IfC>Rfz9(1L*oz#`MTm zOc(lQ59R8`kuzxWf;|dn@X=q+V8j>4O`WPYYOhPX!H?i8{Ho*7=WT~sT1Z#V7l+n_ zA^=-4F!@^X2dJe3IGg<-;9`7>uM#GKIy$@`W^hFuNh2Wq{b*RmAKB@Tei*x=eb1@M zBF2SPEyjjgV&WCTfH~QQ5bx6vdz&GAFutfSzGCU{r+|xj$~=j~GR5(FbNJuu^-W&e zlEsMAE$cP@+!&14M`t~?9h)ZN<1*vk<_O8iM1dH`j`+ z=NU9gBHdmdMidnl{oECZ>e`zrerMDf?B+hI$_yc`I=}XW7~J9({kfnK*;Hd9FK##n zq2~6pmW$tZICEqELsJs3ySSJ9^E0O7dfS8R`+mQz`ynn4fa+rLQOFd(;sw|I=_s&W zX5HqZu=+W>J^Aw!Kc0m0!C>T%@PMqBGPlURT+7TjAcwXG-lF1Sx~O8K`3j?k>-7i( z>Mj0QTkrUCgy_e6P-JjV9OF^%`$K#4TOD13IJ`5Wt8vBq=EwlT(;Wn>qDFl z#YJZI0#?eNP<{(mesnDJukcM5o84ycM*)=&MKwXC!R{K>F>UQ=Bo1ZsAtCi_Kv`() zi;aZrFhUqaooY>6``}4rYcrYge%Uvs6#;?Si%Z}bGj|jtt7#7qKV5V2u~@dW7b?x9 z{}8Mb|XLiq%!9> zm@iPBY1o-YuZz6N2r2G4OWsQPi7k)0a;Mu}mQ=Tt4)DE%0L_ku*Hs7?fsL~Nvl-AS zEmODzc>Wuh6b9JHL9pH2L|Sqm9xyHI$oJUTSnkXHAFbk|k1k8JUZ~9J&H0>WqF_Fd(!KU96c*?n9@U$-) zUc{X=qH}wDd&+?N6%KU%+1XCK#Ul(503E)kq(rQ*8*grJ(}{{w@upz;Us5R9NU+KR zRTM=j9FBy7y}CIN%gD~2IJkb{2~d%)HUA;Fgr+8;oSfWvql?A)zrSQYx98$cPVe}m zVh3MC$4)s1%g^ez;`wxDW@h+3PG3h+iAE(SlXCO$bZngXg9B@7xPRs5(g+EW={LEG z&z0$`sH!5SjLb5_3Ff#@VGw@jP1MW7rI4yB&bavatgxI6;FM2(09wtPai!1K^ z-sfvrs6x|!W^#&`D{Qt+;qVBN=q?sjR^q1cIrIWJGh!r#&jh&b=e6+`$*|rOgVgg! z1^u;`_4O6;y!sbOBl&gppn9w~6qk;}v>$A0X7*wCQx17;ZLM~_J;m?GTbmMtHp;up z!=R-@>`yeG_V3l9)z~3Of15sc0b4EFX`>ftd1yN;ZEageab~ty(0>Xuv$MuS32fJE zAygGsQ*RFbG~&VG@Z-COcUq|1{Mf*w@9F6w7V+kP+qn5kA(~v8^E;lO`lnSjbvQ+17gtpH!3FP)i(+uLM{Ez1kaNKA0NNqa=~umZRItv&06mM zHRGD8BlS7`3C3n)-y=u9xbcW;v@Qk)tA?T>khBTQ6C$ zy!4<5V^9~H#)dk)U`{akwaZ~eTs)kCXnD+M;L%>$G9Fs z82oimJq-qLHdm>9Pbf{UfAuy6DMIE^6u+TLV@YEF&(01o@I*3jR=m%!5?FNtHHtN} zva@OJCxoLml$DkJf6&Pq1N^7qbdY7D+5M=(QW-KR%_L0?Zg*et37La>VP*JSl2}_? zud5={1B%J0=##fX3NIGlA4jCq(^F9QQJz#eYn=)QaW_DvDm^c(OY|CFrSRHG*xA|r zS>#}2!-{24>cvxn1OUG;zI2hXwYRivN1^ie|AIkeygyqCJUgdKJkaUcnbX+_-^SzJ zQD1uiQZ5-N$E_{_;{Hz$a@>y8v}iqDpz^$JQWY&G=c$TI<@cl4dL15-4ce(@L-GUl zzNZJVzrB&4ufnqSfrhL~qp%PT3*u>-qPOFn8yQHF%xWbVOTwXXvlNnP6+vX{Ok${FaEstfP%!w{LK^YPjqR}D1~ca1u6?22M+BTVB{z6Q$@|7TA0ZiQ z3Vm9M_gK9U@wfTprg!_iY8v+~=V$AjN zEXnJuwx>uO(R;9v`@iW{#0DKqGG0tzX=9LK!ft~e<#Q1|5kwmc78yNRk|D1=56#TX z)}n;|wVOl>e^pEqB&n*dg=LI|;L^z$ZjYul4W9RsIjr)v-FyTVFapK@Y2k)WW|?8pKk~O>MiZz zQoE@vo{S#rfu8M0P6Wly)k6Fwq}NcrV)oOgcgE3qZNHTGb45|gKJDq;WD285S5n@d zVJ;~nL7us0sqU8$DqrGew?k_@hFEYQ-!cqIEnku6FLsBYR=5tETxkLfJPfqH6X7 zol-t8^$y%a&Z(sl@rdGqFoSZ*-`Tx(+Si#cAn?72iadnb*ZXH=m6}ZO(os)~@K9y# z76=MCqTCeZq#Mj5vc9ROr%HI!L#xfh!*kt5_2@rm;HR(Em(HjxM<3E}OZHq%?#mtw ziWwplN>i9Ys^LK>IP16j<$bMJ%MtnMp6I)~ySSr27LU%4<5Xb~%9e}SPq2TD+seAS zq$`L&gDLMk()+TdBaWL_9Zqy_ZzM#v?@L{{*tf$lXBVx z>y-qD&#kMescB79nz@G@6R*aRWNx$KUdYSfJ?Imxy1 zPCHJ%oYPfr;_=?gE4scPxV6jNW|cIqdmo9Zx<;cg<(Pm+8ntBKj0LnMdmEQd!x}7D zdS7w*?{J_aD_t#Vl_I2XzXSTi!D1~2mG?0M&_Q^H@)B{7gS6{xNhm!waSxX2*{$qR z&*!O7UmRa=L~j66gMIZ5Eww_=U%#h^g3{6+x@gENLL>F;+G_y4ZS0q~MVSeL)<_zf zkA63QJm4NuldJqUtw_@je%B?IyY%EX79uPCt5UD~WQ$q-j&z)f&AAV^(O_cPL$SpM zj3!K>(IQlx=Z{jXc>f|}1;=SoaXu{tw9ZE#2A5n>hww=$M&!5G4I;r@?@~}zp+!U< z-AeNI0cfpGTX689dUb2gsm)wj&-KYh>}caFqhEUEzi*lU0gw8ZbD5N~+{wk9!3F>e zq(6TA*sb5H;sHo#KRM0NFaP~b;T|ObFZrf#pNvc5tQF>`TdQr$y%;-#-JkTG46>95 zry2M+x>eGiL85`iyXV{BHyFpX129?QxdZ@02F3OD7Wqh6kY8qH;_3-)Nt$5BN0(ZX zcqKnjlG~MyAIu4c4>KLPlL`RVhv%FN3JGnm_+xS8RwVblDqz~+%_PuIe zSzB^w!`g_fiy$BYCxAWnOY2=1Yyoi<*wdjlf(!7ebU|{5BGgFf*XOvVw6T$=aj4NP zFA$EYQEAT=KBb=W4I^baF`Van@1UXeB{S!Gs$AMoNMnB*&<{>lBZ$B zUT4kry&h(IJ_Nd0jrkEF`oweUho8-f!wY$Xm~Tyqr@_IxCF{B09xhP}4449@WW z6jnVxJg41UGDVN4${hDXZJ|MuNjj-n?jm32A;ffxhtG>J3ldN}soUyhWuv@MugzT) zD>|zkK)}G8GHE640D*m6is`2buh#>R^>f7mfY%Zy6u`zzkNa(p7=6)H4j5Ak0jl8) z8Q!l%AI{aJEcFkFj86taEibh+HP?X=7`4BsdD8pa=k6Flw4hM^r;{xokqqvSBtQ%Q z<8{=!%3n5fSktKn^4cFVcK! z9=*%m)^-fG5g_c!nojdWyg^ODw@YKqx_}LI7=m$x&%u`m3qhCu+H(tEvB+G@VQC7- zVM7>C|1S#wxqdkEi!N__6tvwZ$$a<|$CUMXY6j8u|+v!oE6O?D{LpFwt|emr+BA z0Yu_s?_gx^bgK@pXg>tkDCmG18aYrPFVjVJ&{BTJVz-i`PNH`F3M5w;920NjX4sqn z(>_RZ{Gh8?ni?O5Z$!Moju^aLg>_ktJZ3AJFvLc^@TzI1zA-G`qV7?{#+3+~NVvi} zA7aB<5D-A|G$qV(cSA!@6X;(}rKWaNqbl;o;#W3^qKnMO-3&4=(cq>YP9W40jV*^mX z`cS!B*L63Zk~Z|?V@dUB=gQNgJ0PcvI|s*mqSDL*VC(TXM05wbCx!)dK;*~!ZPA=x zzh1NY@o2*VW8#BOvs>xgC1)2Gr@O;?o4xmSA`Azlh~n{W;_tRXJKpGYM}*53vL;dU zA7bC{mV9!wDUay9wfDLO@w>1Gm-`AQ+wd}_+ICuxy?-q6p;)$*ucG)<#RH{0v(>}C*2dCu68EgGj(VMxrub8odgZH_n(!XjD zJvj_f1R)C9V4K{2^+W797E%$u1WO$Ek276=j(*o<-0|Z3>`8bM?*~QQ>ibjp(8-jN zsPwV97^?WnZ=e5|5HRO^DvY|mCnV&NIIVe5j*N`VSDK*wzTIO1AgsiqgJSs^H9Ju* z{&NxDx4QyjM!NUuhvA>^kg!AOBr^0Ipis&;O6lJ|#J(}=`ij-Co$gg((uXa7+jg@> z7MGA<-2a^hH!7%VBa%oanle&J{&OSIa z)ZwHToEE2QHD%7y@OZm_;`emr*FP{2)^D&Z1LE+$cJ#hnuz;-wpi~%kAgbL$C~=^E z51;Q(qW!-08%(owGAl9l1ir*@ZF}(S!s5%6Pkf73&G4fo11EEI?scRNISr%SP>(dd zu=kME#_QusgGMrJUe{c9qrnhCs z`ZyvQetVEUMrG|lq{;FQ&SFMst?j{%B99f#%u;l0hfl@dt4Pq0T#^QkPQUvnE1P0>2V^77Ff0Rmm3C7^%+y z*Vdc3fIwXSrM<6j-d*Bn{zc1Y~%;mXfy6O6u|G6*d zG&Kn~sw7mwixd@9n#GkY{6Z}G?-z*05c=65@8l#)NCMqos7Lzt#-*osIkfc|s+s`k ztiQiMlvD)_bmQ-7X^|5-vd^p%21p-HJL5F0`c3#ptyh$82lKeNxGHXtQ|B9R5pMwP zB@F%m9YDu;(lRU1Gv$oq_*$#b=%WYZUTB` z@;%_4jSEzR+^^aY+y3#A>O`FbvtpU6tas96|3q(Bw&08x0pzTo1iMWYa{3TT*jpAFCJhFjv;@=EdN=L!YKRs2gq{y>s6bVnN%%3u&)y!4=i5w-+K z;`h8h@sHq7D0-(0T9LGO5l$;5`&bRq0@-KhF-q&I}w`I4UrKV2b_D>ZI z$XQZEc;qVvN00jlDwv#r(DfILM&tk3=Z?scIa82apXsPM$!A1#v4quZvbWjSz(9-` zrTa!C$mcTEeduwY3)~cI7<~JpD4umH*14h2bNV3wAvIjAv6{@x&l5@(_UF(}b?PO7 z?PP@jHIDoYi`yO!9e$*xQOqLi-T>9P{=A+*#gRi{YcHQ$ZvBH?Gdeo@@#T`k1^}|S z?NC6hpXyjy1C2IsYtc*ZFODZpf2q&+TI^2d=GhGm4|fB!@0ZWzxA~-mgs*K+4@d9i z<+*ab0Ktvlan(QfcA~2dAd5|NS!8_nxLU&4Z+vR8*1lzU_Qe~6ZF;KG|tmX$4wujQ4Q|aU{G1=M1_!xy2XdD7Qc*)p>^Db zsNxKPw#``V7yrVSBBZyO1d8Ll$L=H0nkhBC-6az zf@#D;Zmhjg6cKIraP~1O+tVB9nA}X)S#t|eoWYDfG#WA!mPR#^pyFPL-D%bi}L5A z5Jukmf&72#q($z+_1+H&pacwlMy0^V3qt%k(+INJBbyk;2(qF|L>jx?3$%YLYx&LY zL%EdhDEJrr2|c(#mJe=H?A$U^m%mZo5}FHdkP5H8p?WO3lgD{5^FV-ltB&%E?-!v<%}tNtRA{S-onVdb#_ zDqzLRGLGg1BcbAqDIIx{C^pc|(WuL>q)(+eH8CyB?~*W;1rbpq zEba}fFg-O$uaU@qz6w)6e~>f6x&E8P-`ypa419nJnkpE2$C{h)%4|gF3GAfnyf(FQ z#WRz>e=k5~Bb6{GtI#UQ>%hxw;}nzgVq$_ehSiUF_>*Q(vcn}p%Z~OJMg9v-jWM94G#h&C?R!q=xsCW+e-j} z8|{o|<=H-aQf{cB77xnI3K?aSwy$r7gObkW|w;8{4BpPSgcY+I;Cj)RYP;eqj+onJC2rYjJ> zo7l_|WBE9|vNut@TDlYYIsfI22d(1FS6c+!#YvYIWS*6ONS4>HDJz#=Gla*cGRp>c z^)w!Ia%o#m6i%0ZGb~(q%f6Dp_Niu{9~s~gc}f{&#lE^hDE>gj zMF^}(t?qELYO+gF)=Big3FQPqp?$Tq+gAq*CW6-1^-C(6nqL7ECh4|yl9EL}78vw$H=vWkjAyIq z>G7`VT!;)>pc^0~Bh#7TVwLrkk4ly_H0eEK*5Z-`AdvXbwU zgR%p%Rvv{+)W&$V#TdtEfeF1#ZT#Fsrg#wdU4*t{^Isq?De>V$nDwJ`>xoo}a}pef zLeO;aP1}mYO%ks_$#l{+*t2C11qAgv{ zK-bF0?}cq?xjK=f6|5`@fh=bhKd9%%UQ>>T1<$I?DG_LMn1> zvLqXa?~{7~FrIU@gbd%>GH(H_v!=m5C-SY~B)rQz(?;y22nD7uBQrg&el(j%{G7-L z9KP7W+p(VB+J7``K{sjHr)BRnNzHWWT6CMVs|af*f5I=OC2Jrf3G(I3uqEm(_NRVl z0+F~*mu$k?l>@tl*${LH2pqz;DF#@7rdl#pp+nf6xWOI+F_coL+;wT$2PZIBcGH5d zqiI6&X|cMGr6WjUl9EBYr=}Jb@YBY) z6KlM^K!!HwazX|s#{^XQ*RBwp{YhcKvDNe3%}wTO*F602G98R)%nsgDhEVS16wYh) ztqP1{dwGg#qyeFu9Z-reOO|;X6lxU#$$k|p8|R#~`FS^TmCmN80m|mmjiGY=Dvf1u z=&dR!4(#amb~s?t)8)I%qPT5p%_(s%+o*a3>+vAjOJTURrKSHEk$4)}hhceI-r$82yY(25XKEviZrM&$xG_vM=S_s^p+})%pL<6 z)$re+#-?+?$#_m&l4DD5BW=)mj3!`p3Wx=D9MfR@G7}cZN*DWS{95li3M6CCTSMNa zzPj{aK4$GOTDUvaCA;_r{TAkteqzbm>e&U0ubirYvV~sAgjR&^)O#U_Z0-#1$I(r4 zXX3Y_d$}lIUS@XX8FQ<|_ssICmgVKWkq`R4IEiHEa}v(dnj&7Kv2!t>lZqZIsmCyE zs~_QYS*RZy_LDMBvRKa?h^VtklfK8z`X*jiC{zeL2@%p6BmWrTToMu&1&o)7lvm@TTWFFl!$kt$J>q_IaLQ=H@m zw--RMo(A#N)2u;!m&6e+E_YeHXD=-MF6v`LBatfWZQUggc#U>n{-}1L=1n=as}TOW zbs1O@E^|Fl0_XqaBoo)hw^zXsh+-&6%ODFm3?s^Jt;=}dI&sS9)n$3nB2RejEpq+f zJM{d8|D*Lf&l-GoMTPPi46;bAU6)-CiJ zDU8}vJ~EOB{#-B9qX1MvilXqj6{&KVOhTXl6hZP9ko__QLT`Of!pe_R-8xEN#1T7P#aH^Pro z-9@-DEyR&4%q5r-rc^p0OCuf+H|OEy)pZW+cJ2w!?!gNS7ts|sia`UCHrNUAw;lkM z{;ZS&{XC;V%wJ(P_PZ{e(49OT1vLy7N9U@nKt6!ypU=BS@;3hcNA6Zy3petAjGRjc zIy50mk`l=mCt)%+iza573U2_zPMdiM7^5*|Z>y@R)E&^zGl8rIWGv;@uH^kBkl6V+ zN06)TIVyYRso*F?JVQ2;UQ$xhFdwJD@k#VqpFOOdbFHQNAEH4}hP<1ofo)mY&T?XEa^<&^zW3 zEn))R7O4hBu;r<9_YktMXP1YIXU@PO$+N<=beNZK3uy63)E;|)quJ5|nHrVx@exi2 zDqV?7rZ@zTLGh>CJt__EQr6Cl4WZ|#d$qTU$`AVHH;SpDK;Ux9qZ@-ag>5e-zw)_f zz10uTk&WJ)bO#L0b*;8rjrnCn7|&A_ywuL1vcWSKQW9he|s&74sBfo0Hdi z340$Rq7h5E-}x2GmIu~nYi+{&;`O{XD6mnQTz9ke``#&QXnZOCRR8AV14wKvUDzbu zWun4n?(GL&lj!?;TE^UKfFXTciUTHLYAX52eQBH)hNo-N4>X#csr+dACBSdT5YrxM zDHWaQ4YlOV#owPR?^6q~U=0Dao6D6~ zQGL)zLZZ##vsTpLhO(vMiXiGLO5$5cB(?p~Y;7r`uzqMFEJrB5u$raJ@tlE`3R<&s z_BrvN4RaY00>>8sMG19du}Tlf0~{=H9lTv({`gB5^9|_Qa14yI-ueVPh*3fE}+^ zG>e)&%85WjHQrX=1L&8v^#knw+R6KNH{Yo`zt*D?lgj7(N7-p!Zmu+t@cRb0j)vs= zf{pFb2m&*iT8%s`^_mOYLq6gbeQB(L)vkn96)3Z_Ya`iwueIS`GCmn(4byuwaTB`I z*ReaD!>21YQi3%AhDL^9f&i;=qJY8&fN7o8VK9ZnM>TJgEkFP<;Iz@5UAaO`+V^wW*^-d7Zecv5AFCZx%Q_PGN|4FVR; zpH(lxg4r1CEy_GkQnbrH!Q_Cz`KQ4t&kiZr0{(cK*!FsQRO)1X64TSLe#r??IqSmG z3fs>DkdHe+)Rg)7;Bvc@oL;0%ardE;F!5H*ET&G&pwjs;o6mJ8F_OW@tX>5H7yWWl z)m((-H7||uoZrEum=Zrg6QLiyN_J){-V6w0x3&zyJ2kZQbCWgMTcGUrjc`Jy(^yoY zzxI59z7xW?;>~65SsZk=-V;&jb!Fe>#!OKToW&D;*i~wxBd~rBo_xh5q5WgW0t95p z9DsHVByw!ZTM5aD+;D7&m4q+wm%WaJHBk7|0rT7G_S}k`-?82KF;2DRYS{}QjdL2k zK(Ob%$`&j!;sl>to5d0JMj0%NPCHNp$wj?R0qybSYr#`L@}$SEyd3KCUSEmp#a0>; zHNPeV7Y0Zt^_%iQ(MpwbDWPb)f5Og@G&}&bvwE=?Sud>nz873VnGOyFyx0fk0rY!@Rnu^^t_#v87Gb zQjPimNy=F+y&A#0VjvD$>m)h@n^|IkM|XEf$!ngOemtpqj~Oo;Y<#+B#?dJo6m_jC zVW{o^IaEnUI@C|+Fbah8HG`g|ke?6_PW!e8H`~8WJnL9Y!k5Z&YiBplPUdqI4dAw@ zfZ;7dF6hd5&=i`W1o$q%Sh6f+UGaMo{@1ks7Kj1tRT9(Xu6%3f4{{?69Qh!)rw#FUF0OCu&3ea=0L&)Co}~I_Js%Z1vMU= zZt0Ypebylc@}@q8$7_4SH%k5}z2|w38mkZjr;HBj?3G%kcDs-Kp1l6Jj0q>8*Z`&^ zkn1F)^1Y^!`9>UIQ`U&wb~{tfVb~4=h(s6MS^AIvvo)EWjrghFz9*T-<`_u6tpXmU ziq_A>Cn%Z(>hrqE<0Jx74-n+r0Qf%)up68nZ?=ow@a5tX6E`aRD7%1|jlGB3M)jy5 z1~9jyD&*g?q&34NSe{_V!Qf~A>N)lD$G)Ck2*@t=QTctFDb{*ErS((rGxOV)V+p{g zkem9hJ7>#5{0#hkA8#z4T@CFkx7lk=`(=^<^1*_S!Au|*iF9Dhz*ipEh~H(#h#{bHu}CYg=|G~w z?tM>W`9lYl1iw!S;$LLvnO=757|P|GR#Dk3T1wd?yv*v9E#o4kD#jj}&y;oGr=kMn z*X17~&nd%y4I9`sC_7AJANl?UyNn7j*b6e{0&)s)2mGGrjMx-{!9dXB!~VM-4B$-5 z07Hh9uCA_>U*>^JoOcV*6i9%+E8=szIw4CHS$rs`T=XattMik|L0_MOkR4jW;s=}o z2TnT>4`!V7!a%4E(GtXT;BaF!&Mc{zoD&fESR)u-I7gYwR@fiHHYFC?o7aW@`am(} z)tY1|xcrs-Ra7rojS3(89_nrux9e}Klkn>wEzrn8XENhGfMHe8kj^c808a*Rn4ZJi zn(2ri!nEPe{N&GR!BywR#d%ee|0Ga=xX7Lhcy7Wv3ZN{fK39J&Jgg#Tzy9DB=RN?rUJbIdE%bdB>)Bh51JCO< zpYTcz!co5M@vz`M8I+P^k>?Y8^R`>Ougt5kNyNqt!VGt? z#Ap@om_*-sfVlk)GYL=SmB$3weecsj4T-{~MSAuP@0PVUCoLo(|CG~ zn1o~@#bZ`7PbEb&vTIA9JJ{3k&XDJQ-Dp$b=Hkb=?Cx~j{tY#uTZ`y$mK*`-)O*y< z!vwl9=EZ_IB$CH zKLcYCEgqed#|}DBgqP)ZS?<=Z>c{EmUJ&~Efzjw5n`7GxABt z>!dQh>$@dPyK^EanBuiBi;V1e?3cf2QK=x1U(e+?5TC%aTcfdOt9Xt3J z#{U|SIbDIcM2o||I}Z##JOg^*-7X&#+_e7=EW^WRXC9jG zca1ScyWFZmXfV$JtX5-~3V%1oH$&)IJz1e*tSTnmH)3D64;9Nb^YdL*xn*?IZ|RZ< z&HWFFGn6-Ix2{7=hqq0)%p40g=HhH;@~*I(uYR{&&_FHBJ$k%eH}HuLDoA6R>xXk7 zEB%d>Ctpg-x6*42_kIqJH*NNS&68awY`WlFL|Bzg=9|G~iv3IGwDi7#m~W2#ODOao zMd^;S9yx%Rsu_4I%q|8o4tk34@&%)`eQRh?=b~w*MV%O)Ov`)=($LW%GV@5 z#a>lam|I=gO+kd&nqKWjR7|X|H{abj6Fs`@U#*0`Q6_h~SuF83x!LE6B7X5pfr;|( zq54x1U%-#2wRJx~Q(BUiYjEU-qncym*3g4{Z%mi^BX9CtMs%tmVG7(?>S|wb%0FjL zbrPfkwuovL>UjIv#+!iXhuh@QoP+ATW9*Z`j>N^D1R^*s6w-&i>7N<}{Q9Rq{mxlH z)F0^ZN&GV_Emub$4WlSMCKx&Xe1Ap&>V}sfWQzQBjt2kn=FOY6P#%`S-PxGPiy^8| zAj7i&qD?3c<*dhn&BMKL{8{;~ZLsRO=;^Qawt8(e|A}PbuEkgA0!OBdx5(Jvh%soV zPK1e!Nd%SWD%8F*-gFequxUt2`!@UBrjW{#smHP|CWpE2cklAIKBDW(c~9tPe%bE4 z$K<-bOZm>_4I@WeU9kHlIjLO6Yp-wCW$@*)(&ZAwSpK!_cFC3NH{i1Wa1CosC|#7U z=t%bQ!S&m$xXw|NBmprRPqn44Z#=YL(p-d&oLM<35YcZzwbud7R;m5uJt*+$I2~8e z{AV1xVA~7<+0H_|fkP_*?2s6sSweDWrFU?!3sLvO1^~>b_-CZMfH6GsZmx}$!u*d6 z57Bs>g3CbW&###*V><G)(UE)a4=z9SBz(GO?GS! zF=}vM^L6@;gG0~C$@wM!QI=Cb-}pS}hNZ#blOuNRVCwgdXDM*eGr~{$lbF`N#rVnn z?3+PtkI;Ep5qEL;DX;}J$KZI%_mEz)9ukXv6j@f`SdpVR}_T=-XaRtQ;JjpgBZ^N_3T;2k3E)H^$B}^!4ws+AZ6&37?bnclgma>g&=bV7|W+|IPa+dWPm#M2$c0Z z@XDKl#|VBMF(y5uK~0&=hJe*81N`+*Qrt*6>sJ{J-6t)PP5@@EuGc5;qFvy7a#M^z zN*b^f^G)PnkhQjnJl1@7`sITtp1`IlEAvm+u*g`pwP=vb50F3aJ!#tcIw&Q34;iW*V;C?LH4eb(<14W&yc{mj)ncfA(h9M^zfCLES zg*fa$I0(hzeMKCho4r8WJpx1tN_fPs4oV+s85t}n+bhK)W*maRUMDGu4#-mg;9`iq zE=AZapl>C*H=2FiNiMq6-PXZD7CK9OTJbQz`_kbf!o$PiDXLQnQQ_OP@4(l=Mw$Vg z1}i)JRgpi^x@F<&Z#Rk?@ZsTviq>gvLw&Lc9+@V~ChCX28{PV+&!-K#U9%TnO2%+k z{Skzzkt2$c%4jWmEbb{TD*t(MQ*a>9;%OG`_z%G-YGP8l*Q=~-CyXSszrISkkFU+! zuN7E$`d{9$nUoc+IFZtE63|33LT2yW$QLkES5L6^U2F@wFrw8KMvJmeRlf zIyzeHGW}^^aQ;d+H=+%y+B;+1g|D8oHz_$7yr*}&{%NM4%EEhNdq*KF2xN?)v#9@Y z2MyY+f=U;^a+HC=4lyy`tStStK>wl0yg&1Q-TXSWW(K>ceV9$<9aPn>T)!dWr+~3N zR)l$Nl-_#q%NJ(aW;^RI2`KDu^LF^*H355?3&rif4}W!ZsE9c`<{1)4TbU*4><8k! zt}Wqs_xPp9_&Mf4D)y7Z=jmv-o7_CN=E*#Ne$%f#TeRTN^mNs|ZP?wCd0*-q2F-#+ zOMK)Ip=aBkXk5(DFJI~WoOjP;S9G7(FwczCo64MeU6)%L9*sG2-xjCtO>q$<(!l7PSud&iQg^^6{k(AZU&rv#gG&6}T-l^jvg zy!-t%Alzv;c6$#U{CQ;+E<6f$?iL18jMb>LHv;oA{y(u&%CTo2CU!|htthp-F^M^A znBmtuDdQaZq!XiMa17<$y*^=2RbV7lnq7~cMs@&2Qnqr?ygB2ch?Gy>{fyY ztgKiR7?PWA0v3u=jLjvEZ7PFYbhibT)z9e}mn|8HO9@qynw1`(F^Ae?Go(=Q<=v(z z3A`u4*f@+~uWao2Tq8vFO@kOSOTyJ#U3EQ2V?V(Wkdbo}+*#Dsyix|!Y3!nCf`UE` zvVgDP9?jz(sVx+Tz8C+yO7%Ye5)S6+gsvNLazR9!RR#S_>Kg`~%fnmvo*3w{0^=D% z^Vd#Nj2WeaB5%;m{J2E_L6ft>_rZ&W7~6^`IeQ~hqEdL3>p?vq??AB;rpj@?Hz?5s z=7e-?1V~y*FyTC?=`r;y-$sktu-3bMOYc59W`HEGXR=d%^QDGD)%p8pYj=P4vIcK9 z#M~jPR!d>skX4v_c~r{wAd}UTS3HS5Rae&Yled1dfciiLJLS`7%z1A>^;B@;) z-MTGJ8mmP}N{Zfz>Xaeg-ze@||I^1ATu&2PoL>I6F!N}-(}0jfZTpfCCKhgxA-y3w zJv}`cZk_x+xwCWsn<6Q{-Zxv-$2^}c(P`OT<`L6Ef(MO^W;EiMJ;|pU!>gE^dP;D7 zsOfyWq$Y52t0O0}a zR$qU#OuFI;hFOoB{MBbFsHRGZM47jS{mM!f6<+>B8<-L|mN^}0PIq*IIc1V)cGoD8 zxn+-3CFKZ9Xhq8F@tS!G`#HC=@LG1?IJ&88kW!I&(^*33z2Cdt5~up91L(~+r(4Yo z|Ed{IR{zO4OExhG{Fwxw=%34?!aa@MV$pV?RLJ2TAfUj)c_W`uNt!d-V>&xVrQX~} z+SboC*eSy zVGuPiA=9%K+}?8Uw`2k15N%B<0UavA`kHW;CkLHw9?Nwvk5ZffI-JZZjjeirHiNX* zkb+Fbx4)ym?wtSBPaMd28b)-{yc=0tpj;B%G$x?8sW(Bil$A}EH+q;e(!q`4%5&fy zKH7PABp-L}%Lb<8YFFfnoYUZ8!nb+nR)L7lq_6y)Rdm#=38PqChR$4G9W`WPL5XYi zvPj^MHqf! z4>JsW8=l2g39V(b^VStXP3K;TsZLIghwaX70vR0#8^T5rol?JKDp8z5!!C`vIEoRz zFWqEbd|P!t+trH^D44JLqyu<*Yi?w_aCUEC1JezN$w z?+L?~A2^8#Tp?MgC)*Ft2`1Dk$)2qDaurZvV?U9q2|OfEbiH|*#Y0NgTwq^*)t`0X z%KcLkDmIQAPlrqhZw#*x;tE$`!Mu7lZ)R-IB@)Jk=V5MO!#QA_k7m)Iki3K@Ehprp z6G?fu@DHXRi#(gxLf22W1Ol^sR>r zPts+{HkRMf_@(#6Ew5Q^zjPgbc(F1>Lwg>Fy6Q`j(!%EI{?6((L&qPia>hPp z99s3Q0QeD>lw!*MjLuZtM;*VUaUK*HMZxQqJ>r(dRyk3D#uG;IzV#3l`ChmsJ6V-1 zl|>$A;PC6DZDArdVU9y3m_XMtGkEwv&L%{eHt~tsi;phxtB|!TWie+{mJne|Vzw)d zdoa4{Zyw(;Pl=ST#)UBpP>aN}p2f2$TDxT>ZV*x2NqWL=C`=!r>k~sgSv}z_1s4#* znH#!A_W%BlOh(-Y%ikxfUBBKFHf2tRkDjHN%I_AB|(ob}}WZdCs~23+6lt}88JUPUM! zIn|Sh91*|u#cWv^CF@F16WJ#YYibacrfH`FZm5z%n?IE-6>S?)#y zA&j~=ZrOm`t?7TSZ+;Nz3+77<+H;TPsyW=h|9g0$Yvw01Z)pfa#dyAe)^%uN0wOra&^^OT28KJLKkCU$Me)}%e`UYz^j#fZ@jAC3gR__W*i)cb{Y&hAps?`vXU6| zmVzF?r~vi>=o_tM9Ip`d7Cz$Ety^?VOjuxRmUD5rx45_n7ZUC+vAE+Yn*Z^mq@LbQ zFb}_a_H!VkzMcZ80jjRn1t&UgZbBOy8+lb#Id%0LRQPhC4i#wg2VQVX!C$_}g1`LY zIB`i(mz@;;5AEJ@O*{=3VO{X~TQ!`EXKd5f;J}G=cA)KmpuH@i6E0z|#M?6=|(ZnV1lMGc-F;FUlVk{~b+7(~@{b%TM0m|VNX z%*+5zKBU?NeIvK+DC<=!!Rzh~Vu7=TT_6yFuLKVU8Ztsav}D0k1lt`*DElMk+_$Fa z;aA1H_U}V5%ylv{>`)n(9#kph)*wx|+s2p~q-G%afYbUYo2Hf))>$#iRwf9a8sX^> zM4{!l-w{C+HLA5}n<<+Sw?7io;<-%+dZ#yV(|%8eJup^bPC0+L_|fITnE2sC*#5pN z0@OkV4$>zfy+ub?S8^_JdRH!_v)V!nqE!;MFPz-&fj_j&=foYj84Ey|!2Km@W|r}0 z(K@1i;WOwN?%E9B`tEzO1>CSy%utEbB0MCvX z;gEC|-4kC90R%7BK@i>l>GJp@LllLb+4JJ;&p!eKh{wS34K1ora=4IABafNa;A1B1 z>FIe>qm^a^R_Q_UldE=PMXjKjgtUbM#YS;+bMvV8??bpPzRUx1C)whArbHw%+0xi& zPihiY32*WH@eRu`?jIHX^(o>$N#N^$VGsWAA`n+xy{{|KFf)MANA5uBSv5b5OCz1x za<2d`s{+0_AtyKY4IfBw8p>rtNz1^SE45x7Eq@a0aR-iE_CNk=qKZ*q`H}3rCr{?z6%|KqrL*D3>CN)&WI$?deDx~c*4L>^p{eZ5Ll9KN9COq*8Fi#9p zP*7YAPvf5oG^oHQy|~1cH$u`I@qT*C3ZAExs^|_TC4gn2AaXvD(YCLc-a_PGMF8AZ zQdLfICWImy8g@p;SI$&Vtc%##*z5s{P&U7Y($>}OhT;7J1ba7J$hDFubMbL;osii< zUH!@h3^YX`3VGAn=S9#QypK0JACLtCz5oX}NAT^LW1}(lm6H9r@-XnWS6Y5WKM6^#^%rU2f z(q2-`Y#fxs`%?`#C_l`fc7mz<2C}l3MZo#UbCI(cU8tHOjM|{g#z2>vVm;10Pxr6fMeQtkOM@K1`2;PBEY{r~; z9YEX7B)ej6_U4PTxeH4G=juDPw6r>{z9zP^lZMiB%gD)9KtNCUbJ5hOYOOEig#`uX zu=w#kcGa`3n6$Tt<^6n!U2KWT=)HsMxz)Eb>LOE0zZSJga2(qMMZzvFKD&*f^(#I# zbpUwad^;gn;J$9xcJK$aw<5`z7d-}jKV0f20H4-{8@d{)_{*wHcLEd$yj`5tBxGb2 z@ZM0+WKHlxV2HMX!E|JEG)P(G(OHb7!uf~1Rf=Bp}=`EjE`mgQ>~Xi7ir&tmv@}E22tR}M@5->irOxkl$pQIop>D^8=Lso9LrxB zXtlYX;a4D08Q_h4xwpH^+XXJFoBXi2wOi~wh3jd8L_2yR6UT{C`pw^g1(S#f;|GYT zBkyK&e)w-KKpNfhjeWw55BM)9JVqxts$RiIC-W=U+~aGg)o)`iXno+Hjq+1wA9sCuHHc|=osXR|27Rp#BjlFkdly|hX zw8|iMm68VAZ{%LU%8us*s8Vb_uc{k>)MOGYntZ7Dk)U802@~#}0rf!snyAcm{nES4 z&8Okl(`c;ynl|Hu$2Sncue3$za?HJq+6 z^9QnaIp8wtqQS0krdbmiFCKv9FLt~fQ!rf*tC60b-kYv;s|GMHYoT7i#>FOge>B1r zS&W5JRNBu7s-3C2d{1}ew7BoN?|kQlfP(fVwFc!haU-`)%nYod9G8il)WnI+qE=T9 zcf~FUBRW_|A&%!TcP94bA3w}NT>$lqCZBpvr<^13BXRxRhAZvNs@7ALb8XqAW2nc%bhQdw#^VTL&2Uv5~xJ~CM-Tu zZ^_=s!aE^*mp$$O<8{Y{N$-yft|xwVHtP>HHHje(OxShRKQ@*aH1@!2Ao3n~booU^ znpK!`bj@dn2H=$R!!;0s>pwd3taO~CNfGgo1q28xU34^%Y(u5kJ}7Y!29abkWK?zD z;G0A)Iq4{?V+_wkC_6Wo0dfjLNVdJ-HHv%23NqC z&)tjHz}nq^CJJ<=$9HFJB;6~+@8UeuZUa{F&51G$kjL`E6V0pdhZu2zi5f>*ko{e{ z@7oy0jf6rEc_p~a3*tLFJIl+<`;V*z0`~`{ZW6oaCo-t(k($}owjiHY{rp{Vqyw`_ zTQLvmDR+7_8LBG%fRMh+xd=S;n;;N+lIFdOD*|LVY*(?HgUg_l`}|NE{;A)Ip%qfhhB;Ark<~tvY`juO?${b@jI9qeVP-h~!uk z&iLu*yGIEuaHL#dSy`Dqr15P6dTIre$ee_PB=h-m_zg|X4($@-{#>Q^ZJX{54m*CJ z_IhhE*x!$`d~*&)nUg1s2|$LJnfM&UaaME~u#*D*CqBtdMoesN9I^m{FpYzX6PU;R zKzR3b#VF7c!i6AvigFng;iWKyn(i|&o`<6vsy0cV_nS|CX?Pc<3OWaLe)+QQ-nhRp z9@(EJkp@U*cwAf@U*X`JH<#lR61pHoYyhD{jhMNs^gnHa=IQMsGmco)rYYN+EAEAk zo!ypc6Ih^&%E}%&H$xe62IZ*L(9lp4fmOs3?_dbiSZg`#q#!-p1i}>6C_MK8|rH}_@}-{YLz#>>|zvW1P)j9?H!H#%;ggg(c6&1D4S6Oe2y2``xe1ZbFF zh|v%v^V!9(duwQD=xJ%iWqSXF=+i#u;W7)k!&YosR&gDOYSB!n_5$~+%yIOmetu!U ziiuqPO+7no#SMmHkY-eIrog-IemoL;d;Gp|%d5B#AHKqOc0q0yNgk2>A5T$W?Vl%E zxCv4f!E)lP2csi)y4{|%UZ0uyAfYbw{HROfcJDaUq>$`r-TMq8#FEI;i;IdzMeA&S z&o)_dl{SFhMj1Q}C4Q(a(PxL7dWVqhbVSCa?KO*piSu>tLORvQ!={3Qw{E|_>$%NB zCSF};^;C(8M1NN|6qCBJCa)(_Qc_MrVtrvx(AmkKjWJ*AEahKgn;9*`9+vv;eQV+l z5VnM2(Vp;LI>QVh5b5Qhop0D18uov@hafV{Luk z&8;ezHw6^go z*f&*ETdV6Pj&^IVMqLVCf>O9$SOCzFgf=)rBlEZ4ZVyYrp2J+T7>)0d8EP&vGID&M zF{#RKym$5wbVP#gn~Cd)YgRDVxrtKw5YN2{4;}qfR2Vy=o+}$y9^wgUV{d!PbIIm7D@0^ zr|yE;yx=O#cP#OAiWfz_3GGVzmU~~X2YhUjFXrPa{T*oi7?_x3p7|U=2D7uAZt(0@ zkn6E<3qHfcKar+oiY+P&7b}jZgB{I`r%K_)Wa>73L$E+`!y>MHjOT}*HJS#I=;qK- z_TN@iG*tJ3tsMy)89G~DbZ?NE4$(XrD*T0!!==-5b~v3JS8)ScpG@y6!w+CEh5SIi zPJr9zkyT@h01a7`sk%abbdFc;B57*SU`DSMqR?5V{6I30BpeFYJ+kQs_lCr;YaOS- zo(bqh`R;*RD$1pc+QSrrkdu}y!&+^tY=u>PY}RLdA9nh2hY_9CfzVeX^ zp;pKqCWtK=!w)KPLfs%C$yqy5sQZy*$Lo_er?TwemCkIkzz-ff<;XN|7Sg(DXiYVL zx=mUpn1P6?zGG_|Og!JmfOY}d*yJLpcJlgEO>Ubs9RR{p z7?FYK^Jf}rIM0!$w!j-u7|3jK{I-v7U4IjRmPD}^)eb8q*bDY7*&|<^zjxxtmrBix znEkakD^bc1^~vqPXHq|wU!(Ha8G5Phy)u4d6XIzvz>Mekzx5!=(1tZ?U9~(QKQ3(lo!fB8_`FHNUI%SDk&Gc5111m&SoD#i%iz@kj0Z_)rfp$3+6?`Nc z>hyTf?+g4iz)7!u6xK998Kgm-U#tUNmG==vd;a~I!eou3$+vw);L9~NvxOsJl`(}@ zxIV6hCa@wUYW7s0Hl%(%ME3REP(Z2;?uZ6a=*SxH_sIM10A_*vJOHF#3ji<(`kjk% z8rGtHAh~78)x(t(uY0`c6iO7 z&tRb{MRy{H-U=X>vV(oB6HMUr9oV)mfzF}e>;!;^J14bjS%vhm z=TV%W6jLe~Bx{cG)y{+Cbm@=x@1NLJ9o$ch(EMv&uS%5sNbeKZ%U-2oV+iI7rxaL3 z7)>w_Iqfb#oa9n~O%((x3lQ%0PrHMJ>p-!nEG(nHL?SYzm~x6TChky+_N6o0;Mxs5 zV2(rdq_=P1*8DjF37P#X?74rorgf@RAxi-!k_@~F7{3GnLIT;$jJ67}C=ePl(d;9F zBx@o^W`Ib$cHs?W6x@7AhqTe|g2tmqFQNJTczzBF;!qG1s*srkdh{#IxAZZ0!ltR2 zvt)0Efpva`Df6hl58oaMvcdzL2h;NdE3JYdZL4H48(B)?iNe34XC z(A^wz@lt;syzlk=d0j3W|JMM=TYPGb3O_xTh+x)3J~e8ET~0@e^^D#~f83%pBWL~G zgm9NILN{dc^mossi9?bXJ>NR1JejT653X?sd;1P}d$`@!HH}2H&7moo2PIU8$;DqC z<+W{E?t+_5nxXNJM%1e6?pKf1r62u%yG9}c8gbe@)aWQCRp(wcBUW-J8e___5+eE7!r;Q4bKpT-Vaw$VMpjIo4M zF`CHDMldEt0(NBAt9l2?x`X1f?AcG17UnKLj+O!*%2WJ!-9`|F6^v9*Z`D_;HsQW@ zd@N9fkR#PHM=u?T&bhu{+A0|v!{9l(-Yvl9gQ)#~N?hC=K9$1ia(>&V&$Vysx3Dc~ zC-+6buF<$Fd2npXd7HzGfu&@5`Eik9o%-H)3w@}6khDC&km*vDK%c-RFxnvGDd6iOu|{&ni_kZR3j z(idYpOuhHTv4#bnPG#HR;%DKGieIXtWZaFj#nwNIiYXa={K4g8e*QNm;|vINDDEi% z=D$-MLB!#v+rcb(Z7cmJ^v>s}$K|I4fAIchbZi`4Mka{=?=Cd2YArFG&N8X`@@Mwh zYv(IHop1gG;^Odq>0tRnoXe&&j`xw?GN;}cv+oZe8N>b95lc}3XhY4G2@+Ju?E$|j zq);FbKV;~^_T&WMXFns^sp#>!VpWFP)#JX05{bnLw5_|!Dk@{Ks*nK>l~tbLoN4`- zNg?2b{Ht}6N~+4roq1`#Z;>OM0MHC{sQ7SaMdIR^0Ky^9OL`uEG(WGg8IjcxPcU+S z&)Mg+CJDPqg+)BAqqleCdL^K92wzZJE7-G-TGz)R=32`bI!@cWkbOJ*_#>4GJ?>Ll z^%S;G?yMq$>T5y5CUv~Lyz}6c8T+6DiSF#wZ; zCXe`x`_+_tCbqfF+6uwi%LDn&%e=ppa7Fa}_gQ612i{YO#IjOX4fnB_<;nIA^z;l> zdS5|98f(z+?Ws!NWH$k(3fY_CF_DXT@d8R10S=k`fw=_tT9FF%r#!mN8I3&nvA*{9 z>{HcGLm_vfpqGV(MNUfydj!ab!d}|>MPh=jsVX84jQfrD9s7VXAjVm+jS1jW3Hn3b zZ4O%ViMnTS)LWs}2}=UR7reM)%UwNSih#CB z2bpO7$ww?-eJi$~V?}r+2kJg=#){xAqVG0&=-V8ctN#X{8pQY7yzl$Ega7X3kPV1X z3hgAW4G1zbUj}+ZeWn!@{yLxcQLC`gz(m(rA#mDxqybBw>t2_6866`d1_+uxVfY`J zm_(-e{>4Rt^#L{B0rYJ3&^mb#mKXS^xlOS<&1p}kDq%vsUB@4L&+`O{@k6wtnQMwG zc7eri$Jqc#h75e4yAh!=e1OU+zVSiH7}x~ujCej zK7T87MUk*T0B(AqHPA29WzSr>GkR1{gQ;K=QXE9_(3E78)BXLuwu9b_5 zI_zin>+c$WlFwlrtzvI^mzWs5&>mFSE09=>X*PLfLsw}WgYQ_O3geS+cfSW%s|xl$ ze*D-9A=;mWjL$DX>EE3DCk5dGhrAzDG2Oal3cb81ATjR}5`t=Gz3_nVdbHI-F!v&) zb$770cQ|$svKZgR#1M_$)2KMuoYWY(dI9DC5rp=-?a2{@agNA;&+Zjh2ZDXlwDPM@ zTw>zHV;|wHc>(saL&EN{fZ@gm3$N7uDvi_UJeRJ?@Z@2a7Zw%*zUgSa`vf+mbKcHg z&COpb1Lv)-5ldiEasHX&!2m`;#GOlwjW)Dm5qNCuG2^B@)u0X(F}K|q`-O$drqr<5h*Spnv-BCnvJp3()*!QZr<>d__N05{7?Fm3}Bu4pO&7G}SoN9%%z zYrxqd0fDDkmaT_6nP4S=WY3EsQPsFo9=iD7psoA{lUw=N8)9%1MLak`_FKF{m)kJo z03h8*g(pz!l`p>yb2S_b=*;T*S*e?u`-NMAGuK4PTMmraK%6lhac zkrFkO5v%Z>PjS#;ocmW9s3DHF4H9DmD^9 z#t`MKWo#UQJO*IWKSSVlIMi?b2f7er1SA1!2UZZUEy`#c!#{tsbj7Yc>iY(-DAFMy zh(4Sem%(}noEc(Uu7&pe9b^%L;ov5%-zhgh*vZRTx%M&9(FkJO&kb&Jbj0?7*r}<+ zy>;Ia0M`2%5Zh3wLdTQFZBcvrGVa?;Ge|`v@B+Qtql|b;y?Zh*=lZ&C$1p7%1c2Z* zF>;7w8R%k!p@mdtq63rLoba+8cp*pF6!s>k;nSzVG()VWh;J;{6*(jZ49w8dID7_j z6G0ciy(pj;TMNgw027C-aY!@{m=Ztz!azfiFn+x&GS?s)y9;540qUcIKBWUf17{z* zDAaj2A(nQXkzhmM17Ou+wKD-v-D+0kBHa7XmN$}7S7cCw&&|z^_{abS3z(x3o-y#fzDY66*uOx#JflUJK>zZph9HaLpE;hD%pe8-tzduzh zS&y9*bv$LyuMH)3r4P=*f9SY$;WbX|63RD>f=@xK87u;i0qRcLNNEA4 zD?U29&Ff&j6F8=BI1XnZIyxFiSQK9xlryEE?2xf+hEdWxhZLz;gu$@~M`uxOZQ^rL znx1_lPO=ktGZ8G7uWKL7Ft=@aXd;7@-`|rNce#dwkKjdh1^2C(1LH%#(+4aXR9^9b zctRb~7abF$2PMUlpvs@d!{2iy`MwXwiz!oDZo;ZAC!A1V0}C?3q0CL=cj|o6x7GH{pMXv5I)DTaXn|-U z{6o1?@^W%AfX0Ue&sQUB(tryCVfqO2CSXDWtE0X@?x#?y%Z_AlBv&omcSipV_(0^r z#yBEwN!lgAKCRBrZU)ly{QM8dNCR=}(^#5}x!?ulOMA7PneF>9uQT=Fn@!mhB8YQ7 zMTZxsh_i*Y0A%!tBNzUvggqEA0{G0tmb(-UQPRp#w!Q>{ae4#dnUt%5BLHC-U1{h0 zEd3Il@lxQ*hC=cmKy*zAqG?Qzwilu$M95w(3A%1gk-*)zU7QYHB>pgDcpd+j0twcH zfB~4%Z#rt6GAiMDs77kuVxd^^sO<2v@iyAzVRC1EBy5|bgYjs1XmcnF8_EA)i*~HN z3BT)~EB1VWId?-ceLrs1>o|jV4mu>H;!vJ_e~$+dFe4TxusjYenG`7<6#Z8F>`aA> zM7wR(<$V){G;WK_znV`R7!7p-hii}OQMR3mTus@>K{sWya(y4_l(ey8iFS|`K4RIwPu)m1*7*7l%%@DL^y5&$q%zU*jTPm|xHqcq+}z;_-7 zb~&VFu%dmS?CQ#k805eZV^F!7y)K~H6>kjqG$B}EuCBMlI+W>dL#?p{^#X63&m?QO zVm?${)PP>`xfxxSQa+0=HvVaMI>>-LFd5z^wc)+3;T$|;-(yQ%_Gpcm_Yt7ufNbwi zdXra{h(-?XD>tie-7looKYcIYNCTSmoKUWe&kIhbljW9>XIf;Ar-{V|F#_*3gYK#|EHC3_AM#iM?XcpRPxo$fKT`i*cD{E&urzcoMw! z54)j@Q@J~%BY65?I6zbQmarQ(F6A_@q*4Z*F{)A4Widc|Xm_!>T14VSRlNR&W-H!xr9P?k%~&Sv z$_!b@ z$&%gpm1YQ7<_@fLi?G#nKwj*vNLumMeE{DkiJrfC3-cZMb_nEgbhrN)t|$*H1V}ty zaTrv~pWoyD9?Y?k#bgZa7B~k@K{f-Y4ke%C2jPgVC%|g)$3E`guC@S?lU>v5)c*^UVd);d?gM>oCi`xse!`d zT0X{wHUv4$C)MXrPxNemq&^RH&j{Cp9P#3JGClV|*c^iPWWy;)#`vYK*MCktx?1$o z>KWOIq5Yv#jytQBi;iuOIkU$nci8+4;E14&>6#MemRNg)yo8()dzkfbWZ1KMRN3H| z1+z!f=@9Z~Ul(#voB_l~P)hiUA}XbaMm3IesmT7evb-ETpi)$`b>KA#;PnKor;{L% zlNx)2=&T@x<}>s@Qu=bOo3IyDLl49cm5f!z`S`SIJOv{xkb|^9aU>@4;*cG5Q3WG6l6z7%ztJpN?R3f~?|C&{t@TX!tUnYS ztDW83ZhZzM5~8Ks21LvPI5%kJctvRbLO#%0_ybkXAR%G0 zQ`c=GeV)YTAgzRkte1uyPP$Cd z2x8cOgCipeUXJfN10@0a283OP=YSPNK&xG;4T?Dim`vP|eu0$Gz%gN?fqn;p>kG#1 zXCR^ik)W(MU}nTyQL~?88LQ+K^0|G@HWw%aUF;(xqi{IN2`-cN|Eq$H?1FR&*nk0C zB&4Jag;y4wCiEbO1@>%p^dk|}8cAQj%qFc@Rw24d=)V!}0E!9L+qVJj6M|zm{~s|e zA}nQQ&IIlh`BksO%@BZAN1D^2)P(Z(C7>4zfX5xqa*cD zr9q*AuvLQ2iwU0_}YE#s~cH2?SBo+YggdM zkK!5r|EbpZyGKEm#^+HAMd;)DTK$TDVlm~aiHf^E!{H;H_GN!r#9Um3exeOOB*-bo zS`g}2a<0-&xe&T=%wEkk+MHxfsSTb;!I6}&!h;{lE52M-`IS`14#h}9`K^(MY110L zU*uL>M7-utr3en$(p0B>S2LD-uq}Dq%8nTr_y5sFw3Z$8VEmEKtfytd3;)#W4zd6JHKRGjOz+aCFNI6*;EoxLw39xI z_WHgs-Wm1x1Kb*JW{%{R;A!I8Uxz@4zn!V=IVV@7*XhD;&8*hbHCe7=4n?QC+f!MI zc)V5=m@yx08*IxVS`0=BOul;>N-dBbL!)AJ&1zR7+7)SAjSfU~PcN#%j6e2>#8wX- zCwBtPUf%DxN1w9qv~G*z?#n2~TmRl0w?-vY%%qJy3&X1XKLY%qWnsKiyiXI<>=ylb z?$$lXFC!d1Vxh1vdls$z^}3>6&Ht9pYd)6_Pv7(?^C{=+jg7<={c zfb9=fxwZO*0~a4I1Q2xN{dF1LKbYw9HUIOOf~3H|l<4M1_jHjP!w-Mj-QQaZaUGV* zuzQ&nKe=cdUcar+g?eJ$4{*fQt(_WKgCdoqe=2#(oI*+V|2*N3b`NhM())v2j0o+W zzAxuyz*c;uSjL}aKPXpa3qWDKA zEOabqoF)}<_-B;pbS*n$kRcEbkojknkYs&@01bMw*k2!mHv*g!_{ino^yYr`)b$+f z3gB6&QTp%Z`9|WFj={p{M~;7n0DkMTCp7A1(n%`FU-hHb6{VwC1t0y4Cpah=$gS#|DKp@SCS2d}mM&NRcjaDi z + + 508 Resource Limit Is Reached + +

Resource Limit Is Reached

+The website is temporarily unable to service your request as it exceeded resource limit. +Please try again later. + diff --git a/docs/images/nf-core-nanoseq_logo_light.png b/docs/images/nf-core-nanoseq_logo_light.png new file mode 100644 index 00000000..1993002f --- /dev/null +++ b/docs/images/nf-core-nanoseq_logo_light.png @@ -0,0 +1,13 @@ + + +507 Insufficient Storage + +

Insufficient Storage

+

The method could not be performed on the resource +because the server is unable to store the +representation needed to successfully complete the +request. There is insufficient free space left in +your storage allocation.

+

Additionally, a 507 Insufficient Storage +error was encountered while trying to use an ErrorDocument to handle the request.

+ diff --git a/docs/output.md b/docs/output.md index 6a367f8d..67030f6a 100644 --- a/docs/output.md +++ b/docs/output.md @@ -230,10 +230,9 @@ The pipeline has special steps which also allow the software versions to be repo Output files * `pipeline_info/` - * Reports generated by the pipeline - `pipeline_report.html`, `pipeline_report.txt` and `software_versions.csv`. - * Reports generated by *Nextflow* - `execution_report.html`, `execution_timeline.html`, `execution_trace.txt` and `pipeline_dag.svg`. - * Reformatted sample sheet files used as input to the pipeline - `samplesheet_reformat.csv`. - * Documentation for interpretation of results in HTML format - `results_description.html`. + * Reports generated by Nextflow: `execution_report.html`, `execution_timeline.html`, `execution_trace.txt` and `pipeline_dag.dot`/`pipeline_dag.svg`. + * Reports generated by the pipeline: `pipeline_report.html`, `pipeline_report.txt` and `software_versions.yml`. The `pipeline_report*` files will only be present if the `--email` / `--email_on_fail` parameter's are used when running the pipeline. + * Reformatted samplesheet files used as input to the pipeline: `samplesheet.valid.csv`. diff --git a/docs/usage.md b/docs/usage.md index cf0ca289..b33902cb 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -330,42 +330,6 @@ process { > **NB:** We specify just the process name i.e. `STAR_ALIGN` in the config file and not the full task name string that is printed to screen in the error message or on the terminal whilst the pipeline is running i.e. `RNASEQ:ALIGN_STAR:STAR_ALIGN`. You may get a warning suggesting that the process selector isn't recognised but you can ignore that if the process name has been specified correctly. This is something that needs to be fixed upstream in core Nextflow. -### Tool-specific options - -For the ultimate flexibility, we have implemented and are using Nextflow DSL2 modules in a way where it is possible for both developers and users to change tool-specific command-line arguments (e.g. providing an additional command-line argument to the `STAR_ALIGN` process) as well as publishing options (e.g. saving files produced by the `STAR_ALIGN` process that aren't saved by default by the pipeline). In the majority of instances, as a user you won't have to change the default options set by the pipeline developer(s), however, there may be edge cases where creating a simple custom config file can improve the behaviour of the pipeline if for example it is failing due to a weird error that requires setting a tool-specific parameter to deal with smaller / larger genomes. - -The command-line arguments passed to STAR in the `STAR_ALIGN` module are a combination of: - -* Mandatory arguments or those that need to be evaluated within the scope of the module, as supplied in the [`script`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/modules/nf-core/software/star/align/main.nf#L49-L55) section of the module file. - -* An [`options.args`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/modules/nf-core/software/star/align/main.nf#L56) string of non-mandatory parameters that is set to be empty by default in the module but can be overwritten when including the module in the sub-workflow / workflow context via the `addParams` Nextflow option. - -The nf-core/rnaseq pipeline has a sub-workflow (see [terminology](https://github.com/nf-core/modules#terminology)) specifically to align reads with STAR and to sort, index and generate some basic stats on the resulting BAM files using SAMtools. At the top of this file we import the `STAR_ALIGN` module via the Nextflow [`include`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/subworkflows/nf-core/align_star.nf#L10) keyword and by default the options passed to the module via the `addParams` option are set as an empty Groovy map [here](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/subworkflows/nf-core/align_star.nf#L5); this in turn means `options.args` will be set to empty by default in the module file too. This is an intentional design choice and allows us to implement well-written sub-workflows composed of a chain of tools that by default run with the bare minimum parameter set for any given tool in order to make it much easier to share across pipelines and to provide the flexibility for users and developers to customise any non-mandatory arguments. - -When including the sub-workflow above in the main pipeline workflow we use the same `include` statement, however, we now have the ability to overwrite options for each of the tools in the sub-workflow including the [`align_options`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/workflows/rnaseq.nf#L225) variable that will be used specifically to overwrite the optional arguments passed to the `STAR_ALIGN` module. In this case, the options to be provided to `STAR_ALIGN` have been assigned sensible defaults by the developer(s) in the pipeline's [`modules.config`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/modules.config#L70-L74) and can be accessed and customised in the [workflow context](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/workflows/rnaseq.nf#L201-L204) too before eventually passing them to the sub-workflow as a Groovy map called `star_align_options`. These options will then be propagated from `workflow -> sub-workflow -> module`. - -As mentioned at the beginning of this section it may also be necessary for users to overwrite the options passed to modules to be able to customise specific aspects of the way in which a particular tool is executed by the pipeline. Given that all of the default module options are stored in the pipeline's `modules.config` as a [`params` variable](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/modules.config#L24-L25) it is also possible to overwrite any of these options via a custom config file. - -Say for example we want to append an additional, non-mandatory parameter (i.e. `--outFilterMismatchNmax 16`) to the arguments passed to the `STAR_ALIGN` module. Firstly, we need to copy across the default `args` specified in the [`modules.config`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/modules.config#L71) and create a custom config file that is a composite of the default `args` as well as the additional options you would like to provide. This is very important because Nextflow will overwrite the default value of `args` that you provide via the custom config. - -As you will see in the example below, we have: - -* appended `--outFilterMismatchNmax 16` to the default `args` used by the module. -* changed the default `publish_dir` value to where the files will eventually be published in the main results directory. -* appended `'bam':''` to the default value of `publish_files` so that the BAM files generated by the process will also be saved in the top-level results directory for the module. Note: `'out':'log'` means any file/directory ending in `out` will now be saved in a separate directory called `my_star_directory/log/`. - -```nextflow -params { - modules { - 'star_align' { - args = "--quantMode TranscriptomeSAM --twopassMode Basic --outSAMtype BAM Unsorted --readFilesCommand zcat --runRNGseed 0 --outFilterMultimapNmax 20 --alignSJDBoverhangMin 1 --outSAMattributes NH HI AS NM MD --quantTranscriptomeBan Singleend --outFilterMismatchNmax 16" - publish_dir = "my_star_directory" - publish_files = ['out':'log', 'tab':'log', 'bam':''] - } - } -} -``` - ### Updating containers The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. If for some reason you need to use a different version of a particular tool with the pipeline then you just need to identify the `process` name and override the Nextflow `container` definition for that process using the `withName` declaration. For example, in the [nf-core/viralrecon](https://nf-co.re/viralrecon) pipeline a tool called [Pangolin](https://github.com/cov-lineages/pangolin) has been used during the COVID-19 pandemic to assign lineages to SARS-CoV-2 genome sequenced samples. Given that the lineage assignments change quite frequently it doesn't make sense to re-release the nf-core/viralrecon everytime a new version of Pangolin has been released. However, you can override the default container used by the pipeline by creating a custom config file and passing it as a command-line argument via `-c custom.config`. diff --git a/modules/local/bam_rename.nf b/modules/local/bam_rename.nf index 54aabeae..940b96a8 100644 --- a/modules/local/bam_rename.nf +++ b/modules/local/bam_rename.nf @@ -1,14 +1,10 @@ -// Import generic module functions -include { saveFiles; getProcessName } from './functions' - process BAM_RENAME { tag "$meta.id" + conda (params.enable_conda ? "conda-forge::sed=4.7" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img" - } else { - container "biocontainers/biocontainers:v1.2.0_cv1" - } + //container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + // 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img' : + // 'quay.io/biocontainers/biocontainers:latest' }" input: tuple val(meta), path(bam) diff --git a/modules/local/bambu.nf b/modules/local/bambu.nf index fab6d607..b51e80e2 100644 --- a/modules/local/bambu.nf +++ b/modules/local/bambu.nf @@ -1,17 +1,8 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process BAMBU { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'') } conda (params.enable_conda ? "conda-forge::r-base=4.0.3 bioconda::bioconductor-bambu=1.0.2 bioconda::bioconductor-bsgenome=1.58.0" : null) - container "docker.io/yuukiiwa/nanoseq:bambu_bsgenome" + container "docker.io/yuukiiwa/nanoseq:bambu_bsgenome" //not on biocontainers; does not have a singularity container input: tuple path(fasta), path(gtf) @@ -32,10 +23,10 @@ process BAMBU { --fasta=$fasta $bams cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: + "${task.process}": r-base: \$(echo \$(R --version 2>&1) | sed 's/^.*R version //; s/ .*\$//') - bioconductor-deseq2: \$(Rscript -e "library(bambu); cat(as.character(packageVersion('bambu')))") - bioconductor-deseq2: \$(Rscript -e "library(BSgenome); cat(as.character(packageVersion('BSgenome')))") + bioconductor-bambu: \$(Rscript -e "library(bambu); cat(as.character(packageVersion('bambu')))") + bioconductor-bsgenome: \$(Rscript -e "library(BSgenome); cat(as.character(packageVersion('BSgenome')))") END_VERSIONS """ } diff --git a/modules/local/bedtools_bamtobed.nf b/modules/local/bedtools_bamtobed.nf index 349c50b5..e4d14fbc 100644 --- a/modules/local/bedtools_bamtobed.nf +++ b/modules/local/bedtools_bamtobed.nf @@ -1,18 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -// params.options = [:] -// def options = initOptions(params.options) - process BEDTOOLS_BAMBED { label 'process_medium' conda (params.enable_conda ? "bioconda::bedtools=2.29.2" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bedtools:2.29.2--hc088bd4_0" - } else { - container "quay.io/biocontainers/bedtools:2.29.2--hc088bd4_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bedtools:2.29.2--hc088bd4_0' : + 'quay.io/biocontainers/bedtools:2.29.2--hc088bd4_0' }" when: !params.skip_alignment && !params.skip_bigbed && (params.protocol == 'directRNA' || params.protocol == 'cDNA') @@ -33,8 +25,8 @@ process BEDTOOLS_BAMBED { -i ${bam[0]} \\ | bedtools sort > ${meta.id}.bed12 cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(bedtools --version | sed -e "s/bedtools v//g") + "${task.process}": + bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") END_VERSIONS """ } diff --git a/modules/local/bedtools_genomecov.nf b/modules/local/bedtools_genomecov.nf index 740f700a..6c510eb5 100644 --- a/modules/local/bedtools_genomecov.nf +++ b/modules/local/bedtools_genomecov.nf @@ -1,18 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -//params.options = [:] -//def options = initOptions(params.options) - process BEDTOOLS_GENOMECOV { label 'process_medium' conda (params.enable_conda ? "bioconda::bedtools=2.29.2" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bedtools:2.29.2--hc088bd4_0" - } else { - container "quay.io/biocontainers/bedtools:2.29.2--hc088bd4_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bedtools:2.29.2--hc088bd4_0' : + 'quay.io/biocontainers/bedtools:2.29.2--hc088bd4_0' }" input: tuple val(meta), path(sizes), val(is_transcripts), path(bam), path(bai) @@ -31,8 +23,8 @@ process BEDTOOLS_GENOMECOV { -bg \\ | bedtools sort > ${meta.id}.bedGraph cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(bedtools --version | sed -e "s/bedtools v//g") + "${task.process}": + bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") END_VERSIONS """ } diff --git a/modules/local/deseq2.nf b/modules/local/deseq2.nf index 97e5ef3c..d4c56330 100644 --- a/modules/local/deseq2.nf +++ b/modules/local/deseq2.nf @@ -1,22 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -params.multiqc_label = '' -def options = initOptions(params.options) - process DESEQ2 { label "process_medium" - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'') } conda (params.enable_conda ? "conda-forge::r-base=4.0.3 bioconda::bioconductor-deseq2=1.28.0" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/mulled-v2-8849acf39a43cdd6c839a369a74c0adc823e2f91:ab110436faf952a33575c64dd74615a84011450b-0" - } else { - container "quay.io/biocontainers/mulled-v2-8849acf39a43cdd6c839a369a74c0adc823e2f91:ab110436faf952a33575c64dd74615a84011450b-0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-8849acf39a43cdd6c839a369a74c0adc823e2f91:ab110436faf952a33575c64dd74615a84011450b-0' : + 'quay.io/biocontainers/mulled-v2-8849acf39a43cdd6c839a369a74c0adc823e2f91:ab110436faf952a33575c64dd74615a84011450b-0' }" input: path counts @@ -30,7 +18,7 @@ process DESEQ2 { run_deseq2.r $params.quantification_method $counts cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: + "${task.process}": r-base: \$(echo \$(R --version 2>&1) | sed 's/^.*R version //; s/ .*\$//') bioconductor-deseq2: \$(Rscript -e "library(DESeq2); cat(as.character(packageVersion('DESeq2')))") END_VERSIONS diff --git a/modules/local/dexseq.nf b/modules/local/dexseq.nf index e6b8a95b..f8bb0e00 100644 --- a/modules/local/dexseq.nf +++ b/modules/local/dexseq.nf @@ -1,15 +1,5 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -params.multiqc_label = '' -def options = initOptions(params.options) - process DEXSEQ { label "process_medium" - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'') } conda (params.enable_conda ? "conda-forge::r-base=4.0.2 bioconda::bioconductor-dexseq=1.36.0 bioconda::bioconductor-drimseq=1.18.0 bioconda::bioconductor-stager=1.12.0" : null) container "docker.io/yuukiiwa/nanoseq:dexseq" @@ -23,18 +13,15 @@ process DEXSEQ { path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def label_lower = params.multiqc_label.toLowerCase() - def label_upper = params.multiqc_label.toUpperCase() """ run_dexseq.r $params.quantification_method $counts cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: + "${task.process}": r-base: \$(echo \$(R --version 2>&1) | sed 's/^.*R version //; s/ .*\$//') - bioconductor-deseq2: \$(Rscript -e "library(DEXSeq); cat(as.character(packageVersion('DEXSeq')))") - bioconductor-deseq2: \$(Rscript -e "library(DRIMSeq); cat(as.character(packageVersion('DRIMSeq')))") - bioconductor-deseq2: \$(Rscript -e "library(stageR); cat(as.character(packageVersion('stageR')))") + bioconductor-dexseq: \$(Rscript -e "library(DEXSeq); cat(as.character(packageVersion('DEXSeq')))") + bioconductor-drimseq: \$(Rscript -e "library(DRIMSeq); cat(as.character(packageVersion('DRIMSeq')))") + bioconductor-stager: \$(Rscript -e "library(stageR); cat(as.character(packageVersion('stageR')))") END_VERSIONS """ } diff --git a/modules/local/functions.nf b/modules/local/functions.nf deleted file mode 100644 index 4860a362..00000000 --- a/modules/local/functions.nf +++ /dev/null @@ -1,75 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/local/get_chrom_sizes.nf b/modules/local/get_chrom_sizes.nf index 596b7c47..5162d7e5 100644 --- a/modules/local/get_chrom_sizes.nf +++ b/modules/local/get_chrom_sizes.nf @@ -1,22 +1,9 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] - -/* - * Get chromosome sizes from a fasta file - */ process GET_CHROM_SIZES { -// publishDir "${params.outdir}", -// mode: params.publish_dir_mode, -// saveAs: { filename -> saveFiles(filename:filename, publish_dir:"genome") } conda (params.enable_conda ? "bioconda::samtools=1.10" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.13--h8c37831_0" - } else { - container "quay.io/biocontainers/samtools:1.13--h8c37831_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.13--h8c37831_0' : + 'quay.io/biocontainers/samtools:1.13--h8c37831_0' }" input: tuple path(fasta), val(name) @@ -32,8 +19,8 @@ process GET_CHROM_SIZES { cut -f 1,2 ${fasta}.fai > ${fasta}.sizes cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ } diff --git a/modules/local/get_nanolyse_fasta.nf b/modules/local/get_nanolyse_fasta.nf index 2d87dbeb..34bc2d54 100644 --- a/modules/local/get_nanolyse_fasta.nf +++ b/modules/local/get_nanolyse_fasta.nf @@ -1,9 +1,3 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process GET_NANOLYSE_FASTA { conda (params.enable_conda ? "conda-forge::sed=4.7" : null) diff --git a/modules/local/get_software_versions.nf b/modules/local/get_software_versions.nf deleted file mode 100644 index 019d496e..00000000 --- a/modules/local/get_software_versions.nf +++ /dev/null @@ -1,33 +0,0 @@ -// Import generic module functions -include { saveFiles } from './functions' - -params.options = [:] - -process GET_SOFTWARE_VERSIONS { - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'pipeline_info', meta:[:], publish_by_meta:[]) } - - conda (params.enable_conda ? "conda-forge::python=3.8.3" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/python:3.8.3" - } else { - container "quay.io/biocontainers/python:3.8.3" - } - - cache false - - input: - path versions - - output: - path "software_versions.tsv" , emit: tsv - path 'software_versions_mqc.yaml', emit: yaml - - script: // This script is bundled with the pipeline, in nf-core/nanoseq/bin/ - """ - echo $workflow.manifest.version > pipeline.version.txt - echo $workflow.nextflow.version > nextflow.version.txt - scrape_software_versions.py &> software_versions_mqc.yaml - """ -} diff --git a/modules/local/get_test_data.nf b/modules/local/get_test_data.nf index 9d0b7ff9..a0b8f606 100644 --- a/modules/local/get_test_data.nf +++ b/modules/local/get_test_data.nf @@ -1,9 +1,3 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process GET_TEST_DATA { container "docker.io/yuukiiwa/git:latest" diff --git a/modules/local/graphmap2_align.nf b/modules/local/graphmap2_align.nf index 2efcb7a4..6bbf264a 100644 --- a/modules/local/graphmap2_align.nf +++ b/modules/local/graphmap2_align.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process GRAPHMAP2_ALIGN { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:meta.id) } conda (params.enable_conda ? "bioconda::graphmap=0.6.3" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/graphmap:0.6.3--he513fc3_0" - } else { - container "quay.io/biocontainers/graphmap:0.6.3--he513fc3_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/graphmap:0.6.3--he513fc3_0' : + 'quay.io/biocontainers/graphmap:0.6.3--he513fc3_0' }" input: tuple val(meta), path(fastq), path(fasta), path(sizes), val(gtf), val(bed), val(is_transcripts), path(index) @@ -41,8 +30,8 @@ process GRAPHMAP2_ALIGN { --extcigar cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(graphmap2 align 2>&1) | sed 's/^.*Version: v//; s/ .*\$//') + "${task.process}": + graphmap2: \$(echo \$(graphmap2 align 2>&1) | sed 's/^.*Version: v//; s/ .*\$//') END_VERSIONS """ } diff --git a/modules/local/graphmap2_index.nf b/modules/local/graphmap2_index.nf index 5df0495c..07d4a182 100644 --- a/modules/local/graphmap2_index.nf +++ b/modules/local/graphmap2_index.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process GRAPHMAP2_INDEX { label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'') } conda (params.enable_conda ? "bioconda::graphmap=0.6.3" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/graphmap:0.6.3--he513fc3_0" - } else { - container "quay.io/biocontainers/graphmap:0.6.3--he513fc3_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/graphmap:0.6.3--he513fc3_0' : + 'quay.io/biocontainers/graphmap:0.6.3--he513fc3_0' }" input: tuple path(fasta), path(sizes), val(gtf), val(bed), val(is_transcripts), val(annotation_str) @@ -37,8 +26,8 @@ process GRAPHMAP2_INDEX { -r $fasta cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(graphmap2 align 2>&1) | sed 's/^.*Version: v//; s/ .*\$//') + "${task.process}": + graphmap2: \$(echo \$(graphmap2 align 2>&1) | sed 's/^.*Version: v//; s/ .*\$//') END_VERSIONS """ } diff --git a/modules/local/gtf2bed.nf b/modules/local/gtf2bed.nf index 17b8fae5..b8918012 100644 --- a/modules/local/gtf2bed.nf +++ b/modules/local/gtf2bed.nf @@ -1,23 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] - -/* - * Convert GTF file to BED format - */ process GTF2BED { label 'process_low' -// publishDir "${params.outdir}", -// mode: params.publish_dir_mode, -// saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'genome', publish_id:'') } conda (params.enable_conda ? "conda-forge::perl=5.26.2" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/perl:5.26.2" - } else { - container "quay.io/biocontainers/perl:5.26.2" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/perl:5.26.2' : + 'quay.io/biocontainers/perl:5.26.2' }" input: tuple path(gtf), val(name) @@ -31,7 +18,7 @@ process GTF2BED { gtf2bed $gtf > ${gtf.baseName}.bed cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: + "${task.process}": perl: \$(echo \$(perl --version 2>&1) | sed 's/.*v\\(.*\\)) built.*/\\1/') END_VERSIONS """ diff --git a/modules/local/guppy.nf b/modules/local/guppy.nf index 04608dd3..4d839923 100644 --- a/modules/local/guppy.nf +++ b/modules/local/guppy.nf @@ -1,14 +1,5 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process GUPPY { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process)) } if (params.guppy_gpu) { container = 'genomicpariscentre/guppy-gpu:4.0.14' @@ -50,8 +41,8 @@ process GUPPY { $model cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(guppy_basecaller --version 2>&1) | sed -r 's/.{81}//') + "${task.process}": + guppy: \$(echo \$(guppy_basecaller --version 2>&1) | sed -r 's/.{81}//') END_VERSIONS ## Concatenate fastq files diff --git a/modules/local/medaka_variant.nf b/modules/local/medaka_variant.nf index 38f32d1b..5b7e75ed 100644 --- a/modules/local/medaka_variant.nf +++ b/modules/local/medaka_variant.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - -process MEDAKA { +process MEDAKA_VARIANT { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:meta.id) } conda (params.enable_conda ? "bioconda::medaka=1.4.4" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/medaka:1.4.4--py38h130def0_0' - } else { - container 'quay.io/biocontainers/medaka:1.4.4--py38h130def0_0' - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/medaka:1.4.4--py38h130def0_0' : + 'quay.io/biocontainers/medaka:1.4.4--py38h130def0_0' }" input: tuple val(meta), path(sizes), val(is_transcripts), path(bam), path(bai) // @@ -27,7 +16,7 @@ process MEDAKA { path "versions.yml" , emit: versions script: - def args = options.args ?: '' + //def args = options.args ?: '' def split_mnps = params.split_mnps ? "-l" : '' def phase_vcf = params.phase_vcf ? "-p" : '' """ @@ -39,7 +28,6 @@ process MEDAKA { -t $task.cpus \\ $split_mnps \\ $phase_vcf \\ - $args cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/minimap2_align.nf b/modules/local/minimap2_align.nf index 5c63ea7d..244a2b92 100644 --- a/modules/local/minimap2_align.nf +++ b/modules/local/minimap2_align.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process MINIMAP2_ALIGN { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'meta.id') } conda (params.enable_conda ? "bioconda::minimap2=2.17" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/minimap2:2.17--hed695b0_3" - } else { - container "quay.io/biocontainers/minimap2:2.17--hed695b0_3" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/minimap2:2.17--hed695b0_3' : + 'quay.io/biocontainers/minimap2:2.17--hed695b0_3' }" input: tuple val(meta), path(fastq), path(fasta), path(sizes), val(gtf), val(bed), val(is_transcripts), path(index) @@ -43,8 +32,8 @@ process MINIMAP2_ALIGN { $fastq > ${meta.id}.sam cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(minimap2 --version 2>&1) + "${task.process}": + minimap2: \$(minimap2 --version 2>&1) END_VERSIONS """ } diff --git a/modules/local/minimap2_index.nf b/modules/local/minimap2_index.nf index dfe1af3f..fb63cac0 100644 --- a/modules/local/minimap2_index.nf +++ b/modules/local/minimap2_index.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process MINIMAP2_INDEX { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'') } conda (params.enable_conda ? "bioconda::minimap2=2.17" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/minimap2:2.17--hed695b0_3" - } else { - container "quay.io/biocontainers/minimap2:2.17--hed695b0_3" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/minimap2:2.17--hed695b0_3' : + 'quay.io/biocontainers/minimap2:2.17--hed695b0_3' }" input: tuple path(fasta), path(sizes), val(gtf), val(bed), val(is_transcripts), val(annotation_str) @@ -40,8 +29,8 @@ process MINIMAP2_INDEX { $fasta cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(minimap2 --version 2>&1) + "${task.process}": + minimap2: \$(minimap2 --version 2>&1) END_VERSIONS """ } diff --git a/modules/local/multiqc.nf b/modules/local/multiqc.nf index 2d595f63..44d8ca11 100644 --- a/modules/local/multiqc.nf +++ b/modules/local/multiqc.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process MULTIQC { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'') } - conda (params.enable_conda ? "bioconda::multiqc=1.9" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/multiqc:1.9--pyh9f0ad1d_0" - } else { - container "quay.io/biocontainers/multiqc:1.9--pyh9f0ad1d_0" - } + conda (params.enable_conda ? "bioconda::multiqc=1.10.1" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.10.1--pyhdfd78af_1' : + 'quay.io/biocontainers/multiqc:1.10.1--pyhdfd78af_1' }" input: path ch_multiqc_config @@ -35,13 +24,18 @@ process MULTIQC { path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' def custom_config = params.multiqc_config ? "--config $multiqc_custom_config" : '' """ - multiqc -f . $custom_config + multiqc \\ + -f \\ + $args \\ + $custom_config \\ + . cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$( multiqc --version | sed -e "s/multiqc, version //g" ) + "${task.process}": + multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) END_VERSIONS """ } diff --git a/modules/local/nanoplot.nf b/modules/local/nanoplot.nf new file mode 100644 index 00000000..6172371b --- /dev/null +++ b/modules/local/nanoplot.nf @@ -0,0 +1,41 @@ +process NANOPLOT { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? 'bioconda::nanoplot=1.38.0' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/nanoplot:1.38.0--pyhdfd78af_0' : + 'quay.io/biocontainers/nanoplot:1.38.0--pyhdfd78af_0' }" + + input: + tuple val(meta), path(ontfile) + + output: + tuple val(meta), path("$output_html"), emit: html + tuple val(meta), path("$output_png") , emit: png + tuple val(meta), path("$output_txt") , emit: txt + tuple val(meta), path("$output_log") , emit: log + path "versions.yml" , emit: versions + + script: + //def args = task.ext.args ?: '' + // $options.args \\ + def input_file = ("$ontfile".endsWith(".fastq.gz")) ? "--fastq ${ontfile}" : + ("$ontfile".endsWith(".txt")) ? "--summary ${ontfile}" : '' + def output_dir = ("$ontfile".endsWith(".fastq.gz")) ? "fastq/${meta.id}" : + ("$ontfile".endsWith(".txt")) ? "summary" : '' + output_html = output_dir+"/*.html" + output_png = output_dir+"/*.png" + output_txt = output_dir+"/*.txt" + output_log = output_dir+"/*.log" + """ + NanoPlot \\ + -t $task.cpus \\ + $input_file \\ + -o $output_dir + cat <<-END_VERSIONS > versions.yml + "${task.process}": + nanoplot: \$(echo \$(NanoPlot --version 2>&1) | sed 's/^.*NanoPlot //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/modules/local/qcat.nf b/modules/local/qcat.nf index e9ec7d8f..fe2cbc07 100644 --- a/modules/local/qcat.nf +++ b/modules/local/qcat.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process QCAT { tag "$input_path" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:input_path) } conda (params.enable_conda ? "bioconda::qcat=1.1.0" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/qcat:1.1.0--py_0" - } else { - container "quay.io/biocontainers/qcat:1.1.0--py_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/qcat:1.1.0--py_0' : + 'quay.io/biocontainers/qcat:1.1.0--py_0' }" input: path input_path @@ -47,8 +36,8 @@ process QCAT { gzip fastq/* cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(qcat --version 2>&1 | sed 's/^.*qcat //; s/ .*\$//') + "${task.process}": + qcat: \$(qcat --version 2>&1 | sed 's/^.*qcat //; s/ .*\$//') END_VERSIONS """ } diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf index 8d74185d..9aaecf0c 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -1,31 +1,27 @@ -// Import generic module functions -include { saveFiles } from './functions' - -params.options = [:] - process SAMPLESHEET_CHECK { tag "$samplesheet" - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'pipeline_info', meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "conda-forge::python=3.8.3" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/python:3.8.3" - } else { - container "quay.io/biocontainers/python:3.8.3" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/python:3.8.3' : + 'quay.io/biocontainers/python:3.8.3' }" input: path samplesheet output: - path '*.csv' + path '*.csv' , emit: csv + path "versions.yml", emit: versions script: // This script is bundled with the pipeline, in nf-core/nanoseq/bin/ """ check_samplesheet.py \\ $samplesheet \\ samplesheet.valid.csv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python --version | sed 's/Python //g') + END_VERSIONS """ } diff --git a/modules/local/samtools_sort_index.nf b/modules/local/samtools_sort_index.nf index f16c2906..619f0158 100644 --- a/modules/local/samtools_sort_index.nf +++ b/modules/local/samtools_sort_index.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process SAMTOOLS_SORT_INDEX { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::samtools=1.14" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0" - } else { - container "quay.io/biocontainers/samtools:1.14--hb421002_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: tuple val(meta), path(bam) @@ -27,15 +16,14 @@ process SAMTOOLS_SORT_INDEX { path "versions.yml" , emit: versions script: - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" """ - samtools sort $options.args -@ $task.cpus -o ${prefix}.sorted.bam -T $prefix $bam + samtools sort -@ $task.cpus -o ${meta.id}.sorted.bam -T $meta.id $bam - samtools index $options.args ${prefix}.sorted.bam + samtools index ${meta.id}.sorted.bam cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ } diff --git a/modules/local/samtools_view_bam.nf b/modules/local/samtools_view_bam.nf index 5982ebfa..791b0143 100644 --- a/modules/local/samtools_view_bam.nf +++ b/modules/local/samtools_view_bam.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process SAMTOOLS_VIEW_BAM { tag "$meta.id" label 'process_medium' -// publishDir "${params.outdir}", -// mode: params.publish_dir_mode, -// saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'meta.id') } conda (params.enable_conda ? "bioconda::samtools=1.10" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.11--h6270b1f_0" - } else { - container "quay.io/biocontainers/samtools:1.11--h6270b1f_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: tuple val(meta), path(sizes), val(is_transcripts), path(sam) @@ -30,8 +19,8 @@ process SAMTOOLS_VIEW_BAM { samtools view -b -h -O BAM -@ $task.cpus -o ${meta.id}.bam $sam cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ } diff --git a/modules/local/sniffles.nf b/modules/local/sniffles.nf index 317eede5..a54addeb 100644 --- a/modules/local/sniffles.nf +++ b/modules/local/sniffles.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process SNIFFLES { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:meta.id) } conda (params.enable_conda ? "bioconda::sniffles=1.0.12" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/sniffles:1.0.12--h8b12597_1' - } else { - container 'quay.io/biocontainers/sniffles:1.0.12--h8b12597_1' - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/sniffles:1.0.12--h8b12597_1' : + 'quay.io/biocontainers/sniffles:1.0.12--h8b12597_1' }" input: tuple val(meta), path(sizes), val(is_transcripts), path(bam), path(bai) @@ -35,8 +24,8 @@ process SNIFFLES { -t $task.cpus cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$( echo \$(featureCounts -v 2>&1) | sed -e "s/featureCounts v//g") + "${task.process}": + sniffles: \$( echo \$(featureCounts -v 2>&1) | sed -e "s/featureCounts v//g") //need sniffles's version END_VERSIONS """ } diff --git a/modules/local/stringtie2.nf b/modules/local/stringtie2.nf index 322eadf0..2493fdb9 100644 --- a/modules/local/stringtie2.nf +++ b/modules/local/stringtie2.nf @@ -1,23 +1,12 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process STRINGTIE2 { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:meta.id) } // Note: 2.7X indices incompatible with AWS iGenomes. conda (params.enable_conda ? "bioconda::stringtie=2.1.4" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/stringtie:2.1.4--h7e0af3c_0" - } else { - container "quay.io/biocontainers/stringtie:2.1.4--h7e0af3c_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/stringtie:2.1.4--h7e0af3c_0' : + 'quay.io/biocontainers/stringtie:2.1.4--h7e0af3c_0' }" input: tuple val(meta), path(fasta), path(gtf), path(bam) @@ -34,8 +23,8 @@ process STRINGTIE2 { -o ${meta.id}.stringtie.gtf $bam cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(stringtie --version 2>&1) + "${task.process}": + stringtie2: \$(stringtie --version 2>&1) END_VERSIONS """ } diff --git a/modules/local/subread_featurecounts.nf b/modules/local/subread_featurecounts.nf index fa722727..a00b0f73 100644 --- a/modules/local/subread_featurecounts.nf +++ b/modules/local/subread_featurecounts.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - process SUBREAD_FEATURECOUNTS { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'') } // Note: 2.7X indices incompatible with AWS iGenomes. conda (params.enable_conda ? "bioconda::subread=2.0.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/subread:2.0.1--hed695b0_0" - } else { - container "quay.io/biocontainers/subread:2.0.1--hed695b0_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/subread:2.0.1--hed695b0_0' : + 'quay.io/biocontainers/subread:2.0.1--hed695b0_0' }" input: path gtf @@ -58,8 +47,8 @@ process SUBREAD_FEATURECOUNTS { $bams cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$( echo \$(featureCounts -v 2>&1) | sed -e "s/featureCounts v//g") + "${task.process}": + featureCounts: \$( echo \$(featureCounts -v 2>&1) | sed -e "s/featureCounts v//g") END_VERSIONS """ } diff --git a/modules/local/ucsc_bed12tobigbed.nf b/modules/local/ucsc_bed12tobigbed.nf index 6f31caef..e8f20075 100644 --- a/modules/local/ucsc_bed12tobigbed.nf +++ b/modules/local/ucsc_bed12tobigbed.nf @@ -1,24 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -def options = initOptions(params.options) - -def VERSION = '377' - process UCSC_BED12TOBIGBED { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'bigBed', publish_id:'meta.id') } conda (params.enable_conda ? "bioconda::ucsc-bedtobigbed=377" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/ucsc-bedtobigbed:377--h446ed27_1" - } else { - container "quay.io/biocontainers/ucsc-bedtobigbed:377--h446ed27_1" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ucsc-bedtobigbed:377--h446ed27_1' : + 'quay.io/biocontainers/ucsc-bedtobigbed:377--h446ed27_1' }" when: !params.skip_alignment && !params.skip_bigbed && (params.protocol == 'directRNA' || params.protocol == 'cDNA') @@ -31,6 +18,7 @@ process UCSC_BED12TOBIGBED { path "versions.yml" , emit: versions script: + def VERSION = '377' """ bedToBigBed \\ $bed12 \\ @@ -38,8 +26,8 @@ process UCSC_BED12TOBIGBED { ${meta.id}.bigBed cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo $VERSION) + "${task.process}": + ucsc_bed12tobigbed: \$(echo $VERSION) END_VERSIONS """ } diff --git a/modules/local/ucsc_bedgraphtobigwig.nf b/modules/local/ucsc_bedgraphtobigwig.nf index 28622203..41d4eb81 100644 --- a/modules/local/ucsc_bedgraphtobigwig.nf +++ b/modules/local/ucsc_bedgraphtobigwig.nf @@ -1,24 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -// def options = initOptions(params.options) //this line gives "Cannot get property 'args' on null object" - -def VERSION = '377' - process UCSC_BEDGRAPHTOBIGWIG { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), publish_id:'meta.id') } conda (params.enable_conda ? "bioconda::ucsc-bedgraphtobigwig=377" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/ucsc-bedgraphtobigwig:377--h446ed27_1" - } else { - container "quay.io/biocontainers/ucsc-bedgraphtobigwig:377--h446ed27_1" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ucsc-bedgraphtobigwig:377--h446ed27_1' : + 'quay.io/biocontainers/ucsc-bedgraphtobigwig:377--h446ed27_1' }" input: tuple val(meta), path(sizes), path(bedgraph) @@ -28,12 +15,13 @@ process UCSC_BEDGRAPHTOBIGWIG { path "versions.yml" , emit: versions script: + def VERSION = '377' """ bedGraphToBigWig $bedgraph $sizes ${meta.id}.bigWig cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo $VERSION) + "${task.process}": + ucsc_bedgraphtobigwig: \$(echo $VERSION) END_VERSIONS """ } diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf b/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf deleted file mode 100644 index 85628ee0..00000000 --- a/modules/nf-core/modules/custom/dumpsoftwareversions/functions.nf +++ /dev/null @@ -1,78 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf index faf2073f..934bb467 100644 --- a/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process CUSTOM_DUMPSOFTWAREVERSIONS { label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'pipeline_info', meta:[:], publish_by_meta:[]) } // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container conda (params.enable_conda ? "bioconda::multiqc=1.11" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0" - } else { - container "quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0' }" input: path versions @@ -27,80 +16,6 @@ process CUSTOM_DUMPSOFTWAREVERSIONS { path "versions.yml" , emit: versions script: - """ - #!/usr/bin/env python - - import yaml - import platform - from textwrap import dedent - - def _make_versions_html(versions): - html = [ - dedent( - '''\\ - - - - - - - - - - ''' - ) - ] - for process, tmp_versions in sorted(versions.items()): - html.append("") - for i, (tool, version) in enumerate(sorted(tmp_versions.items())): - html.append( - dedent( - f'''\\ - - - - - - ''' - ) - ) - html.append("") - html.append("
Process Name Software Version
{process if (i == 0) else ''}{tool}{version}
") - return "\\n".join(html) - - module_versions = {} - module_versions["${getProcessName(task.process)}"] = { - 'python': platform.python_version(), - 'yaml': yaml.__version__ - } - - with open("$versions") as f: - workflow_versions = yaml.load(f, Loader=yaml.BaseLoader) | module_versions - - workflow_versions["Workflow"] = { - "Nextflow": "$workflow.nextflow.version", - "$workflow.manifest.name": "$workflow.manifest.version" - } - - versions_mqc = { - 'id': 'software_versions', - 'section_name': '${workflow.manifest.name} Software Versions', - 'section_href': 'https://github.com/${workflow.manifest.name}', - 'plot_type': 'html', - 'description': 'are collected at run time from the software output.', - 'data': _make_versions_html(workflow_versions) - } - - with open("software_versions.yml", 'w') as f: - yaml.dump(workflow_versions, f, default_flow_style=False) - with open("software_versions_mqc.yml", 'w') as f: - yaml.dump(versions_mqc, f, default_flow_style=False) - - with open('versions.yml', 'w') as f: - yaml.dump(module_versions, f, default_flow_style=False) - """ + def args = task.ext.args ?: '' + template 'dumpsoftwareversions.py' } diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml index c8310e35..5b5b8a60 100644 --- a/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml @@ -31,3 +31,4 @@ output: authors: - "@drpatelh" + - "@grst" diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py new file mode 100644 index 00000000..d1390392 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +import yaml +import platform +from textwrap import dedent + + +def _make_versions_html(versions): + html = [ + dedent( + """\\ + + + + + + + + + + """ + ) + ] + for process, tmp_versions in sorted(versions.items()): + html.append("") + for i, (tool, version) in enumerate(sorted(tmp_versions.items())): + html.append( + dedent( + f"""\\ + + + + + + """ + ) + ) + html.append("") + html.append("
Process Name Software Version
{process if (i == 0) else ''}{tool}{version}
") + return "\\n".join(html) + + +versions_this_module = {} +versions_this_module["${task.process}"] = { + "python": platform.python_version(), + "yaml": yaml.__version__, +} + +with open("$versions") as f: + versions_by_process = yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module + +# aggregate versions by the module name (derived from fully-qualified process name) +versions_by_module = {} +for process, process_versions in versions_by_process.items(): + module = process.split(":")[-1] + try: + assert versions_by_module[module] == process_versions, ( + "We assume that software versions are the same between all modules. " + "If you see this error-message it means you discovered an edge-case " + "and should open an issue in nf-core/tools. " + ) + except KeyError: + versions_by_module[module] = process_versions + +versions_by_module["Workflow"] = { + "Nextflow": "$workflow.nextflow.version", + "$workflow.manifest.name": "$workflow.manifest.version", +} + +versions_mqc = { + "id": "software_versions", + "section_name": "${workflow.manifest.name} Software Versions", + "section_href": "https://github.com/${workflow.manifest.name}", + "plot_type": "html", + "description": "are collected at run time from the software output.", + "data": _make_versions_html(versions_by_module), +} + +with open("software_versions.yml", "w") as f: + yaml.dump(versions_by_module, f, default_flow_style=False) +with open("software_versions_mqc.yml", "w") as f: + yaml.dump(versions_mqc, f, default_flow_style=False) + +with open("versions.yml", "w") as f: + yaml.dump(versions_this_module, f, default_flow_style=False) diff --git a/modules/nf-core/modules/fastqc/functions.nf b/modules/nf-core/modules/fastqc/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/fastqc/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/fastqc/main.nf b/modules/nf-core/modules/fastqc/main.nf index 39c327b2..d250eca0 100644 --- a/modules/nf-core/modules/fastqc/main.nf +++ b/modules/nf-core/modules/fastqc/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process FASTQC { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::fastqc=0.11.9" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/fastqc:0.11.9--0" - } else { - container "quay.io/biocontainers/fastqc:0.11.9--0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/fastqc:0.11.9--0' : + 'quay.io/biocontainers/fastqc:0.11.9--0' }" input: tuple val(meta), path(reads) @@ -24,24 +13,32 @@ process FASTQC { output: tuple val(meta), path("*.html"), emit: html tuple val(meta), path("*.zip") , emit: zip - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' // Add soft-links to original FastQs for consistent naming in pipeline - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def prefix = task.ext.prefix ?: "${meta.id}" if (meta.single_end) { """ [ ! -f ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz - fastqc $options.args --threads $task.cpus ${prefix}.fastq.gz - fastqc --version | sed -e "s/FastQC v//g" > ${software}.version.txt + fastqc $args --threads $task.cpus ${prefix}.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" ) + END_VERSIONS """ } else { """ [ ! -f ${prefix}_1.fastq.gz ] && ln -s ${reads[0]} ${prefix}_1.fastq.gz [ ! -f ${prefix}_2.fastq.gz ] && ln -s ${reads[1]} ${prefix}_2.fastq.gz - fastqc $options.args --threads $task.cpus ${prefix}_1.fastq.gz ${prefix}_2.fastq.gz - fastqc --version | sed -e "s/FastQC v//g" > ${software}.version.txt + fastqc $args --threads $task.cpus ${prefix}_1.fastq.gz ${prefix}_2.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" ) + END_VERSIONS """ } } diff --git a/modules/nf-core/modules/fastqc/meta.yml b/modules/nf-core/modules/fastqc/meta.yml index 8eb9953d..b09553a3 100644 --- a/modules/nf-core/modules/fastqc/meta.yml +++ b/modules/nf-core/modules/fastqc/meta.yml @@ -15,6 +15,7 @@ tools: overrepresented sequences. homepage: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/ documentation: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/ + licence: ['GPL-2.0-only'] input: - meta: type: map @@ -40,10 +41,10 @@ output: type: file description: FastQC report archive pattern: "*_{fastqc.zip}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@grst" diff --git a/modules/nf-core/modules/multiqc/functions.nf b/modules/nf-core/modules/multiqc/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/multiqc/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/multiqc/main.nf b/modules/nf-core/modules/multiqc/main.nf deleted file mode 100644 index da780800..00000000 --- a/modules/nf-core/modules/multiqc/main.nf +++ /dev/null @@ -1,35 +0,0 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - -process MULTIQC { - label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } - - conda (params.enable_conda ? "bioconda::multiqc=1.10.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/multiqc:1.10.1--py_0" - } else { - container "quay.io/biocontainers/multiqc:1.10.1--py_0" - } - - input: - path multiqc_files - - output: - path "*multiqc_report.html", emit: report - path "*_data" , emit: data - path "*_plots" , optional:true, emit: plots - path "*.version.txt" , emit: version - - script: - def software = getSoftwareName(task.process) - """ - multiqc -f $options.args . - multiqc --version | sed -e "s/multiqc, version //g" > ${software}.version.txt - """ -} diff --git a/modules/nf-core/modules/multiqc/meta.yml b/modules/nf-core/modules/multiqc/meta.yml deleted file mode 100644 index 532a8bb1..00000000 --- a/modules/nf-core/modules/multiqc/meta.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: MultiQC -description: Aggregate results from bioinformatics analyses across many samples into a single report -keywords: - - QC - - bioinformatics tools - - Beautiful stand-alone HTML report -tools: - - multiqc: - description: | - MultiQC searches a given directory for analysis logs and compiles a HTML report. - It's a general use tool, perfect for summarising the output from numerous bioinformatics tools. - homepage: https://multiqc.info/ - documentation: https://multiqc.info/docs/ -input: - - multiqc_files: - type: file - description: | - List of reports / files recognised by MultiQC, for example the html and zip output of FastQC -output: - - report: - type: file - description: MultiQC report file - pattern: "multiqc_report.html" - - data: - type: dir - description: MultiQC data dir - pattern: "multiqc_data" - - plots: - type: file - description: Plots created by MultiQC - pattern: "*_data" - - version: - type: file - description: File containing software version - pattern: "*.{version.txt}" -authors: - - "@abhi18av" - - "@bunop" - - "@drpatelh" diff --git a/modules/nf-core/modules/nanolyse/functions.nf b/modules/nf-core/modules/nanolyse/functions.nf deleted file mode 100644 index 85628ee0..00000000 --- a/modules/nf-core/modules/nanolyse/functions.nf +++ /dev/null @@ -1,78 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} diff --git a/modules/nf-core/modules/nanolyse/main.nf b/modules/nf-core/modules/nanolyse/main.nf index 271592f7..c52d9e9d 100644 --- a/modules/nf-core/modules/nanolyse/main.nf +++ b/modules/nf-core/modules/nanolyse/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process NANOLYSE { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::nanolyse=1.2.0" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/nanolyse:1.2.0--py_0" - } else { - container "quay.io/biocontainers/nanolyse:1.2.0--py_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/nanolyse:1.2.0--py_0' : + 'quay.io/biocontainers/nanolyse:1.2.0--py_0' }" input: tuple val(meta), path(fastq) @@ -28,14 +17,15 @@ process NANOLYSE { path "versions.yml" , emit: versions script: - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ gunzip -c $fastq | NanoLyse -r $fasta | gzip > ${prefix}.fastq.gz mv NanoLyse.log ${prefix}.nanolyse.log - cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(NanoLyse --version 2>&1 | sed -e "s/NanoLyse //g") + "${task.process}": + nanolyse: \$(NanoLyse --version 2>&1 | sed -e "s/NanoLyse //g") END_VERSIONS """ } + diff --git a/modules/nf-core/modules/nanoplot/functions.nf b/modules/nf-core/modules/nanoplot/functions.nf deleted file mode 100644 index 85628ee0..00000000 --- a/modules/nf-core/modules/nanoplot/functions.nf +++ /dev/null @@ -1,78 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} diff --git a/modules/nf-core/modules/nanoplot/main.nf b/modules/nf-core/modules/nanoplot/main.nf deleted file mode 100644 index cce9e036..00000000 --- a/modules/nf-core/modules/nanoplot/main.nf +++ /dev/null @@ -1,51 +0,0 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - -process NANOPLOT { - tag "$meta.id" - label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - - conda (params.enable_conda ? 'bioconda::nanoplot=1.38.0' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/nanoplot:1.38.0--pyhdfd78af_0" - } else { - container "quay.io/biocontainers/nanoplot:1.38.0--pyhdfd78af_0" - } - - input: - tuple val(meta), path(ontfile) - - output: - tuple val(meta), path("$output_html"), emit: html - tuple val(meta), path("$output_png") , emit: png - tuple val(meta), path("$output_txt") , emit: txt - tuple val(meta), path("$output_log") , emit: log - path "versions.yml" , emit: versions - - script: - def input_file = ("$ontfile".endsWith(".fastq.gz")) ? "--fastq ${ontfile}" : - ("$ontfile".endsWith(".txt")) ? "--summary ${ontfile}" : '' - def output_dir = ("$ontfile".endsWith(".fastq.gz")) ? "fastq/${meta.id}" : - ("$ontfile".endsWith(".txt")) ? "summary" : '' - output_html = output_dir+"/*.html" - output_png = output_dir+"/*.png" - output_txt = output_dir+"/*.txt" - output_log = output_dir+"/*.log" - """ - NanoPlot \\ - $options.args \\ - -t $task.cpus \\ - $input_file \\ - -o $output_dir - cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(NanoPlot --version 2>&1) | sed 's/^.*NanoPlot //; s/ .*\$//') - END_VERSIONS - """ -} diff --git a/modules/nf-core/modules/nanoplot/meta.yml b/modules/nf-core/modules/nanoplot/meta.yml deleted file mode 100644 index 52ebb622..00000000 --- a/modules/nf-core/modules/nanoplot/meta.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: nanoplot -description: Run NanoPlot on nanopore-sequenced reads -keywords: - - quality control - - qc - - fastq - - sequencing summary - - nanopore -tools: - - nanoplot: - description: | - NanoPlot is a tool for ploting long-read sequencing data and - alignment. - homepage: http://nanoplot.bioinf.be - documentation: https://github.com/wdecoster/NanoPlot - licence: ['GPL-3.0-or-later'] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - fastq: - type: file - description: | - List of input basecalled-FastQ files. - - summary_txt: - type: file - description: | - List of sequencing_summary.txt files from running basecalling. -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - html: - type: file - description: NanoPlot report - pattern: "*{.html}" - - png: - type: file - description: Plots generated by NanoPlot - pattern: "*{.png}" - - txt: - type: file - description: Stats from NanoPlot - pattern: "*{.txt}" - - log: - type: file - description: log file of NanoPlot run - pattern: "*{.log}" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@drpatelh" - - "@yuukiiwa" diff --git a/modules/nf-core/modules/pycoqc/functions.nf b/modules/nf-core/modules/pycoqc/functions.nf deleted file mode 100644 index 85628ee0..00000000 --- a/modules/nf-core/modules/pycoqc/functions.nf +++ /dev/null @@ -1,78 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} diff --git a/modules/nf-core/modules/pycoqc/main.nf b/modules/nf-core/modules/pycoqc/main.nf index 2c263d61..ab18a3fc 100644 --- a/modules/nf-core/modules/pycoqc/main.nf +++ b/modules/nf-core/modules/pycoqc/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process PYCOQC { tag "$summary" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "bioconda::pycoqc=2.5.2" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/pycoqc:2.5.2--py_0" - } else { - container "quay.io/biocontainers/pycoqc:2.5.2--py_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pycoqc:2.5.2--py_0' : + 'quay.io/biocontainers/pycoqc:2.5.2--py_0' }" input: path summary @@ -27,16 +16,17 @@ process PYCOQC { path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' """ pycoQC \\ - $options.args \\ + $args \\ -f $summary \\ -o pycoqc.html \\ -j pycoqc.json - cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(pycoQC --version 2>&1 | sed 's/^.*pycoQC v//; s/ .*\$//') + "${task.process}": + pycoqc: \$(pycoQC --version 2>&1 | sed 's/^.*pycoQC v//; s/ .*\$//') END_VERSIONS """ } + diff --git a/modules/nf-core/modules/samtools/flagstat/functions.nf b/modules/nf-core/modules/samtools/flagstat/functions.nf deleted file mode 100644 index 85628ee0..00000000 --- a/modules/nf-core/modules/samtools/flagstat/functions.nf +++ /dev/null @@ -1,78 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} diff --git a/modules/nf-core/modules/samtools/flagstat/main.nf b/modules/nf-core/modules/samtools/flagstat/main.nf index f9115c6b..2b62e31e 100644 --- a/modules/nf-core/modules/samtools/flagstat/main.nf +++ b/modules/nf-core/modules/samtools/flagstat/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_FLAGSTAT { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::samtools=1.13' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.13--h8c37831_0" - } else { - container "quay.io/biocontainers/samtools:1.13--h8c37831_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: tuple val(meta), path(bam), path(bai) @@ -26,11 +15,13 @@ process SAMTOOLS_FLAGSTAT { path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' """ - samtools flagstat $bam > ${bam}.flagstat + samtools flagstat --threads ${task.cpus-1} $bam > ${bam}.flagstat cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ } + diff --git a/modules/nf-core/modules/samtools/flagstat/meta.yml b/modules/nf-core/modules/samtools/flagstat/meta.yml index 6703c392..adb182e9 100644 --- a/modules/nf-core/modules/samtools/flagstat/meta.yml +++ b/modules/nf-core/modules/samtools/flagstat/meta.yml @@ -16,22 +16,7 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 -params: - - outdir: - type: string - description: | - The pipeline's output directory. By default, the module will - output files into `$params.outdir/` - - publish_dir_mode: - type: string - description: | - Value for the Nextflow `publishDir` mode parameter. - Available: symlink, rellink, link, copy, copyNoFollow, move. - - enable_conda: - type: boolean - description: | - Run the module with Conda using the software specified - via the `conda` directive + licence: ['MIT'] input: - meta: type: map @@ -56,9 +41,10 @@ output: type: file description: File containing samtools flagstat output pattern: "*.{flagstat}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" + diff --git a/modules/nf-core/modules/samtools/idxstats/functions.nf b/modules/nf-core/modules/samtools/idxstats/functions.nf deleted file mode 100644 index ce468a6f..00000000 --- a/modules/nf-core/modules/samtools/idxstats/functions.nf +++ /dev/null @@ -1,79 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} - diff --git a/modules/nf-core/modules/samtools/idxstats/main.nf b/modules/nf-core/modules/samtools/idxstats/main.nf index b005088a..e4c6623e 100644 --- a/modules/nf-core/modules/samtools/idxstats/main.nf +++ b/modules/nf-core/modules/samtools/idxstats/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_IDXSTATS { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::samtools=1.13' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.13--h8c37831_0" - } else { - container "quay.io/biocontainers/samtools:1.13--h8c37831_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: tuple val(meta), path(bam), path(bai) @@ -26,11 +15,13 @@ process SAMTOOLS_IDXSTATS { path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' """ samtools idxstats $bam > ${bam}.idxstats cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ } + diff --git a/modules/nf-core/modules/samtools/idxstats/meta.yml b/modules/nf-core/modules/samtools/idxstats/meta.yml index c33f1724..334a1d51 100644 --- a/modules/nf-core/modules/samtools/idxstats/meta.yml +++ b/modules/nf-core/modules/samtools/idxstats/meta.yml @@ -17,22 +17,7 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 -params: - - outdir: - type: string - description: | - The pipeline's output directory. By default, the module will - output files into `$params.outdir/` - - publish_dir_mode: - type: string - description: | - Value for the Nextflow `publishDir` mode parameter. - Available: symlink, rellink, link, copy, copyNoFollow, move. - - enable_conda: - type: boolean - description: | - Run the module with Conda using the software specified - via the `conda` directive + licence: ['MIT'] input: - meta: type: map @@ -57,9 +42,10 @@ output: type: file description: File containing samtools idxstats output pattern: "*.{idxstats}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" + diff --git a/modules/nf-core/modules/samtools/index/functions.nf b/modules/nf-core/modules/samtools/index/functions.nf deleted file mode 100644 index 85628ee0..00000000 --- a/modules/nf-core/modules/samtools/index/functions.nf +++ /dev/null @@ -1,78 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} diff --git a/modules/nf-core/modules/samtools/index/main.nf b/modules/nf-core/modules/samtools/index/main.nf index febbc11c..eb79d3d3 100644 --- a/modules/nf-core/modules/samtools/index/main.nf +++ b/modules/nf-core/modules/samtools/index/main.nf @@ -1,37 +1,29 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_INDEX { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::samtools=1.13' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.13--h8c37831_0" - } else { - container "quay.io/biocontainers/samtools:1.13--h8c37831_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: - tuple val(meta), path(bam) + tuple val(meta), path(input) output: - tuple val(meta), path("*.bai"), optional:true, emit: bai - tuple val(meta), path("*.csi"), optional:true, emit: csi - path "versions.yml" , emit: versions + tuple val(meta), path("*.bai") , optional:true, emit: bai + tuple val(meta), path("*.csi") , optional:true, emit: csi + tuple val(meta), path("*.crai"), optional:true, emit: crai + path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' """ - samtools index $options.args $bam + samtools index -@ ${task.cpus-1} $args $input cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ } + diff --git a/modules/nf-core/modules/samtools/index/meta.yml b/modules/nf-core/modules/samtools/index/meta.yml index 8d3e049c..c4d2da1c 100644 --- a/modules/nf-core/modules/samtools/index/meta.yml +++ b/modules/nf-core/modules/samtools/index/meta.yml @@ -14,22 +14,7 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 -params: - - outdir: - type: string - description: | - The pipeline's output directory. By default, the module will - output files into `$params.outdir/` - - publish_dir_mode: - type: string - description: | - Value for the Nextflow `publishDir` mode parameter. - Available: symlink, rellink, link, copy, copyNoFollow, move. - - enable_conda: - type: boolean - description: | - Run the module with Conda using the software specified - via the `conda` directive + licence: ['MIT'] input: - meta: type: map @@ -50,10 +35,20 @@ output: type: file description: BAM/CRAM/SAM index file pattern: "*.{bai,crai,sai}" - - version: + - crai: + type: file + description: BAM/CRAM/SAM index file + pattern: "*.{bai,crai,sai}" + - csi: + type: file + description: CSI index file + pattern: "*.{csi}" + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@ewels" + - "@maxulysse" + diff --git a/modules/nf-core/modules/samtools/sort/functions.nf b/modules/nf-core/modules/samtools/sort/functions.nf deleted file mode 100644 index 85628ee0..00000000 --- a/modules/nf-core/modules/samtools/sort/functions.nf +++ /dev/null @@ -1,78 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} diff --git a/modules/nf-core/modules/samtools/sort/main.nf b/modules/nf-core/modules/samtools/sort/main.nf index bfe8d8a1..ad981e0c 100644 --- a/modules/nf-core/modules/samtools/sort/main.nf +++ b/modules/nf-core/modules/samtools/sort/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_SORT { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::samtools=1.13' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.13--h8c37831_0" - } else { - container "quay.io/biocontainers/samtools:1.13--h8c37831_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: tuple val(meta), path(bam) @@ -26,12 +15,14 @@ process SAMTOOLS_SORT { path "versions.yml" , emit: versions script: - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ - samtools sort $options.args -@ $task.cpus -o ${prefix}.sorted.bam -T $prefix $bam + samtools sort $args -@ $task.cpus -o ${prefix}.sorted.bam -T $prefix $bam cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ } + diff --git a/modules/nf-core/modules/samtools/sort/meta.yml b/modules/nf-core/modules/samtools/sort/meta.yml index e8f74b26..78431457 100644 --- a/modules/nf-core/modules/samtools/sort/meta.yml +++ b/modules/nf-core/modules/samtools/sort/meta.yml @@ -14,22 +14,7 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 -params: - - outdir: - type: string - description: | - The pipeline's output directory. By default, the module will - output files into `$params.outdir/` - - publish_dir_mode: - type: string - description: | - Value for the Nextflow `publishDir` mode parameter. - Available: symlink, rellink, link, copy, copyNoFollow, move. - - enable_conda: - type: boolean - description: | - Run the module with Conda using the software specified - via the `conda` directive + licence: ['MIT'] input: - meta: type: map @@ -50,10 +35,11 @@ output: type: file description: Sorted BAM/CRAM/SAM file pattern: "*.{bam,cram,sam}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@ewels" + diff --git a/modules/nf-core/modules/samtools/stats/functions.nf b/modules/nf-core/modules/samtools/stats/functions.nf deleted file mode 100644 index 85628ee0..00000000 --- a/modules/nf-core/modules/samtools/stats/functions.nf +++ /dev/null @@ -1,78 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} diff --git a/modules/nf-core/modules/samtools/stats/main.nf b/modules/nf-core/modules/samtools/stats/main.nf index 6218dd2d..596197c0 100644 --- a/modules/nf-core/modules/samtools/stats/main.nf +++ b/modules/nf-core/modules/samtools/stats/main.nf @@ -1,36 +1,29 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_STATS { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::samtools=1.13' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.13--h8c37831_0" - } else { - container "quay.io/biocontainers/samtools:1.13--h8c37831_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: - tuple val(meta), path(bam), path(bai) + tuple val(meta), path(input), path(input_index) + //path fasta output: tuple val(meta), path("*.stats"), emit: stats path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' + //def reference = fasta ? "--reference ${fasta}" : "" """ - samtools stats $bam > ${bam}.stats + samtools stats --threads ${task.cpus-1} ${input} > ${input}.stats cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ } + diff --git a/modules/nf-core/modules/samtools/stats/meta.yml b/modules/nf-core/modules/samtools/stats/meta.yml index 9f478cc0..de9ab166 100644 --- a/modules/nf-core/modules/samtools/stats/meta.yml +++ b/modules/nf-core/modules/samtools/stats/meta.yml @@ -15,36 +15,25 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 -params: - - outdir: - type: string - description: | - The pipeline's output directory. By default, the module will - output files into `$params.outdir/` - - publish_dir_mode: - type: string - description: | - Value for the Nextflow `publishDir` mode parameter. - Available: symlink, rellink, link, copy, copyNoFollow, move. - - enable_conda: - type: boolean - description: | - Run the module with Conda using the software specified - via the `conda` directive + licence: ['MIT'] input: - meta: type: map description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM/CRAM/SAM file - pattern: "*.{bam,cram,sam}" - - bai: - type: file - description: Index for BAM/CRAM/SAM file - pattern: "*.{bai,crai,sai}" + - input: + type: file + description: BAM/CRAM file from alignment + pattern: "*.{bam,cram}" + - input_index: + type: file + description: BAI/CRAI file from alignment + pattern: "*.{bai,crai}" + - fasta: + type: optional file + description: Reference file the CRAM was created with + pattern: "*.{fasta,fa}" output: - meta: type: map @@ -55,9 +44,11 @@ output: type: file description: File containing samtools stats output pattern: "*.{stats}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" + - "@FriederikeHanssen" + diff --git a/modules/nf-core/modules/stringtie/merge/functions.nf b/modules/nf-core/modules/stringtie/merge/functions.nf deleted file mode 100644 index 85628ee0..00000000 --- a/modules/nf-core/modules/stringtie/merge/functions.nf +++ /dev/null @@ -1,78 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Extract name of module from process name using $task.process -// -def getProcessName(task_process) { - return task_process.tokenize(':')[-1] -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - - // Do not publish versions.yml unless running from pytest workflow - if (args.filename.equals('versions.yml') && !System.getenv("NF_CORE_MODULES_TEST")) { - return null - } - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } -} diff --git a/modules/nf-core/modules/stringtie/merge/main.nf b/modules/nf-core/modules/stringtie/merge/main.nf index 371533bb..82e5ea58 100644 --- a/modules/nf-core/modules/stringtie/merge/main.nf +++ b/modules/nf-core/modules/stringtie/merge/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName; getProcessName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process STRINGTIE_MERGE { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } // Note: 2.7X indices incompatible with AWS iGenomes. conda (params.enable_conda ? "bioconda::stringtie=2.1.7" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/stringtie:2.1.7--h978d192_0" - } else { - container "quay.io/biocontainers/stringtie:2.1.7--h978d192_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/stringtie:2.1.7--h978d192_0' : + 'quay.io/biocontainers/stringtie:2.1.7--h978d192_0' }" input: path stringtie_gtf @@ -27,15 +16,16 @@ process STRINGTIE_MERGE { path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' """ stringtie \\ --merge $stringtie_gtf \\ -G $annotation_gtf \\ -o stringtie.merged.gtf - cat <<-END_VERSIONS > versions.yml - ${getProcessName(task.process)}: - ${getSoftwareName(task.process)}: \$(stringtie --version 2>&1) + "${task.process}": + stringtie: \$(stringtie --version 2>&1) END_VERSIONS """ } + diff --git a/nextflow.config b/nextflow.config index 26d0188f..ef2ce0d0 100644 --- a/nextflow.config +++ b/nextflow.config @@ -70,7 +70,6 @@ params { // Options: Custom config custom_config_version = 'master' custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}" - hostnames = [:] config_profile_description = null config_profile_contact = null config_profile_url = null @@ -78,12 +77,10 @@ params { // Options: Other help = false outdir = './results' - enable_conda = false - publish_dir_mode = 'copy' - igenomes_base = 's3://ngi-igenomes/igenomes/' - igenomes_ignore = false - max_multiqc_email_size = '25.MB' - multiqc_title = null + igenomes_base = 's3://ngi-igenomes/igenomes/' //note + igenomes_ignore = false //note + max_multiqc_email_size = '25.MB' //note + multiqc_title = null //note tracedir = "${params.outdir}/pipeline_info" email = null email_on_fail = null @@ -92,14 +89,12 @@ params { help = false validate_params = true show_hidden_params = false - schema_ignore_params = 'genomes,modules' + schema_ignore_params = 'genomes' enable_conda = false - singularity_pull_docker_container = false // Config options custom_config_version = 'master' custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}" - hostnames = [:] config_profile_description = null config_profile_contact = null config_profile_url = null @@ -115,9 +110,6 @@ params { // Load base.config by default for all pipelines includeConfig 'conf/base.config' -// Load modules.config for DSL2 module specific options -includeConfig 'conf/modules.config' - // Load nf-core custom profiles from different Institutions try { includeConfig "${params.custom_config_base}/nfcore_custom.config" @@ -125,13 +117,6 @@ try { System.err.println("WARNING: Could not load nf-core/config profiles: ${params.custom_config_base}/nfcore_custom.config") } -// Load igenomes.config if required -if (!params.igenomes_ignore) { - includeConfig 'conf/igenomes.config' -} else { - params.genomes = [:] -} - profiles { debug { process.beforeScript = 'echo $HOSTNAME' } conda { @@ -188,11 +173,22 @@ profiles { test_nobc_nodx_vc { includeConfig 'conf/test_nobc_nodx_vc.config' } } +// Load igenomes.config if required +if (!params.igenomes_ignore) { + includeConfig 'conf/igenomes.config' +} else { + params.genomes = [:] +} + // Export these variables to prevent local Python/R libraries from conflicting with those in the container +// The JULIA depot path has been adjusted to a fixed path `/usr/local/share/julia` that needs to be used for packages in the container. +// See https://apeltzer.github.io/post/03-julia-lang-nextflow/ for details on that. Once we have a common agreement on where to keep Julia packages, this is adjustable. + env { PYTHONNOUSERSITE = 1 R_PROFILE_USER = "/.Rprofile" R_ENVIRON_USER = "/.Renviron" + JULIA_DEPOT_PATH = "/usr/local/share/julia" } // Capture exit codes from upstream processes when piping @@ -222,10 +218,13 @@ manifest { homePage = 'https://github.com/nf-core/nanoseq' description = 'A pipeline to demultiplex, QC and map Nanopore data' mainScript = 'main.nf' - nextflowVersion = '!>=21.04.0' + nextflowVersion = '!>=21.10.3' version = '2.0.1' } +// Load modules.config for DSL2 module specific options +includeConfig 'conf/modules.config' + // Function to ensure that resource requirements don't go beyond // a maximum limit def check_max(obj, type) { diff --git a/subworkflows/local/align_graphmap2.nf b/subworkflows/local/align_graphmap2.nf index 6723214e..7b175c17 100644 --- a/subworkflows/local/align_graphmap2.nf +++ b/subworkflows/local/align_graphmap2.nf @@ -2,12 +2,8 @@ * Alignment with GRAPHMAP2 */ -params.index_options = [:] -params.align_options = [:] -params.samtools_options = [:] - -include { GRAPHMAP2_INDEX } from '../../modules/local/graphmap2_index' addParams( options: params.index_options ) -include { GRAPHMAP2_ALIGN } from '../../modules/local/graphmap2_align' addParams( options: params.align_options ) +include { GRAPHMAP2_INDEX } from '../../modules/local/graphmap2_index' +include { GRAPHMAP2_ALIGN } from '../../modules/local/graphmap2_align' workflow ALIGN_GRAPHMAP2 { take: diff --git a/subworkflows/local/align_minimap2.nf b/subworkflows/local/align_minimap2.nf index 8e1598a8..70693a8a 100644 --- a/subworkflows/local/align_minimap2.nf +++ b/subworkflows/local/align_minimap2.nf @@ -2,12 +2,8 @@ * Alignment with MINIMAP2 */ -params.index_options = [:] -params.align_options = [:] -params.samtools_options = [:] - -include { MINIMAP2_INDEX } from '../../modules/local/minimap2_index' addParams( options: params.index_options ) -include { MINIMAP2_ALIGN } from '../../modules/local/minimap2_align' addParams( options: params.align_options ) +include { MINIMAP2_INDEX } from '../../modules/local/minimap2_index' +include { MINIMAP2_ALIGN } from '../../modules/local/minimap2_align' workflow ALIGN_MINIMAP2 { take: diff --git a/subworkflows/local/bam_sort_index_samtools.nf b/subworkflows/local/bam_sort_index_samtools.nf index c7ecc2b5..9971a86a 100644 --- a/subworkflows/local/bam_sort_index_samtools.nf +++ b/subworkflows/local/bam_sort_index_samtools.nf @@ -2,13 +2,11 @@ * Sort, index BAM file and run samtools stats, flagstat and idxstats */ -params.options = [:] - -include { SAMTOOLS_VIEW_BAM } from '../../modules/local/samtools_view_bam' addParams( options: params.options ) -include { SAMTOOLS_SORT } from '../../modules/nf-core/modules/samtools/sort/main' addParams( options: params.options ) -include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' addParams( options: params.options ) -include { SAMTOOLS_SORT_INDEX } from '../../modules/local/samtools_sort_index' addParams( options: params.options ) -include { BAM_STATS_SAMTOOLS } from '../../subworkflows/nf-core/bam_stats_samtools' addParams( options: params.options ) +include { SAMTOOLS_VIEW_BAM } from '../../modules/local/samtools_view_bam' +include { SAMTOOLS_SORT } from '../../modules/nf-core/modules/samtools/sort/main' +include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' +include { SAMTOOLS_SORT_INDEX } from '../../modules/local/samtools_sort_index' +include { BAM_STATS_SAMTOOLS } from '../../subworkflows/nf-core/bam_stats_samtools' workflow BAM_SORT_INDEX_SAMTOOLS { take: diff --git a/subworkflows/local/bedtools_ucsc_bigbed.nf b/subworkflows/local/bedtools_ucsc_bigbed.nf index 90c8a081..0a8d94b3 100644 --- a/subworkflows/local/bedtools_ucsc_bigbed.nf +++ b/subworkflows/local/bedtools_ucsc_bigbed.nf @@ -2,10 +2,8 @@ * Convert BAM to BigBed */ -params.bigbed_options = [:] - -include { BEDTOOLS_BAMBED } from '../../modules/local/bedtools_bamtobed' addParams( options: params.bigbed_options ) -include { UCSC_BED12TOBIGBED } from '../../modules/local/ucsc_bed12tobigbed' addParams( options: params.bigbed_options ) +include { BEDTOOLS_BAMBED } from '../../modules/local/bedtools_bamtobed' +include { UCSC_BED12TOBIGBED } from '../../modules/local/ucsc_bed12tobigbed' workflow BEDTOOLS_UCSC_BIGBED { take: diff --git a/subworkflows/local/bedtools_ucsc_bigwig.nf b/subworkflows/local/bedtools_ucsc_bigwig.nf index be9a90b0..ab45a51e 100644 --- a/subworkflows/local/bedtools_ucsc_bigwig.nf +++ b/subworkflows/local/bedtools_ucsc_bigwig.nf @@ -2,10 +2,8 @@ * Convert BAM to BigWig */ -params.bigwig_options = [:] - -include { BEDTOOLS_GENOMECOV } from '../../modules/local/bedtools_genomecov' addParams( options: params.bigwig_options ) -include { UCSC_BEDGRAPHTOBIGWIG } from '../../modules/local/ucsc_bedgraphtobigwig' addParams( options: params.bigwig_options ) +include { BEDTOOLS_GENOMECOV } from '../../modules/local/bedtools_genomecov' +include { UCSC_BEDGRAPHTOBIGWIG } from '../../modules/local/ucsc_bedgraphtobigwig' workflow BEDTOOLS_UCSC_BIGWIG { take: diff --git a/subworkflows/local/differential_deseq2_dexseq.nf b/subworkflows/local/differential_deseq2_dexseq.nf index 9e7279b4..df42c79e 100644 --- a/subworkflows/local/differential_deseq2_dexseq.nf +++ b/subworkflows/local/differential_deseq2_dexseq.nf @@ -2,12 +2,8 @@ * Differential Expression Analysis with DESeq2 and DEXSeq */ -params.deseq2_options = [:] -params.dexseq_options = [:] - -include { DESEQ2 } from '../../modules/local/deseq2' addParams( options: params.deseq2_options ) -include { DEXSEQ } from '../../modules/local/dexseq' addParams( options: params.dexseq_options ) - +include { DESEQ2 } from '../../modules/local/deseq2' +include { DEXSEQ } from '../../modules/local/dexseq' workflow DIFFERENTIAL_DESEQ2_DEXSEQ { take: diff --git a/subworkflows/local/dna_variant_calling.nf b/subworkflows/local/dna_variant_calling.nf index 3ad02329..27d1ffd1 100644 --- a/subworkflows/local/dna_variant_calling.nf +++ b/subworkflows/local/dna_variant_calling.nf @@ -1,11 +1,9 @@ /* * DNA variant calling */ -params.medaka_vc_options = [:] -params.sniffles_sv_options = [:] -include { MEDAKA } from '../../modules/local/medaka_variant' addParams( options: params.medaka_vc_options ) -include { SNIFFLES } from '../../modules/local/sniffles' addParams( options: params.sniffles_sv_options ) +include { MEDAKA_VARIANT } from '../../modules/local/medaka_variant' +include { SNIFFLES } from '../../modules/local/sniffles' workflow DNA_VARIANT_CALLING { @@ -37,8 +35,8 @@ workflow DNA_VARIANT_CALLING { /* * Call variants with MEDAKA */ - MEDAKA( ch_view_sortbam, ch_index ) - ch_variant_calls = MEDAKA.out.variant_calls + MEDAKA_VARIANT( ch_view_sortbam, ch_index ) + ch_variant_calls = MEDAKA_VARIANT.out.variant_calls } ch_sv_calls = Channel.empty() if (!skip_sniffles){ diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 4a55975d..a7031cbc 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -2,9 +2,7 @@ * Check input samplesheet and get read channels */ -params.options = [:] - -include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' addParams( options: params.options ) +include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' workflow INPUT_CHECK { take: @@ -12,6 +10,7 @@ workflow INPUT_CHECK { main: SAMPLESHEET_CHECK ( samplesheet ) + .csv .splitCsv ( header:true, sep:',' ) .map { get_sample_info(it, params.genomes) } .map { it -> [ it[0], it[2], it[3], it[4], it[5], it[6], it[1] , it[7] ] } diff --git a/subworkflows/local/prepare_genome.nf b/subworkflows/local/prepare_genome.nf index 4c57a4d0..f415dea1 100644 --- a/subworkflows/local/prepare_genome.nf +++ b/subworkflows/local/prepare_genome.nf @@ -2,10 +2,8 @@ * Prepare genome/transcriptome before alignment */ -params.genome_options = [:] - -include { GET_CHROM_SIZES } from '../../modules/local/get_chrom_sizes' addParams( options: params.genome_options ) -include { GTF2BED } from '../../modules/local/gtf2bed' addParams( options: params.genome_options ) +include { GET_CHROM_SIZES } from '../../modules/local/get_chrom_sizes' +include { GTF2BED } from '../../modules/local/gtf2bed' workflow PREPARE_GENOME { take: diff --git a/subworkflows/nf-core/qcbasecall_pycoqc_nanoplot.nf b/subworkflows/local/qcbasecall_pycoqc_nanoplot.nf similarity index 83% rename from subworkflows/nf-core/qcbasecall_pycoqc_nanoplot.nf rename to subworkflows/local/qcbasecall_pycoqc_nanoplot.nf index d6c2f971..397a8572 100644 --- a/subworkflows/nf-core/qcbasecall_pycoqc_nanoplot.nf +++ b/subworkflows/local/qcbasecall_pycoqc_nanoplot.nf @@ -2,11 +2,8 @@ * Basecalling QC with PycoQC and NanoPlot */ -params.pycoqc_options = [:] -params.nanoplot_summary_options = [:] - -include { PYCOQC } from '../../modules/nf-core/modules/pycoqc/main' addParams( options: params.pycoqc_options ) -include { NANOPLOT } from '../../modules/nf-core/modules/nanoplot/main' addParams( options: params.nanoplot_summary_options ) +include { PYCOQC } from '../../modules/nf-core/modules/pycoqc/main' +include { NANOPLOT } from '../../modules/local/nanoplot' workflow QCBASECALL_PYCOQC_NANOPLOT { take: diff --git a/subworkflows/nf-core/qcfastq_nanoplot_fastqc.nf b/subworkflows/local/qcfastq_nanoplot_fastqc.nf similarity index 86% rename from subworkflows/nf-core/qcfastq_nanoplot_fastqc.nf rename to subworkflows/local/qcfastq_nanoplot_fastqc.nf index 135e00ca..a719abf9 100644 --- a/subworkflows/nf-core/qcfastq_nanoplot_fastqc.nf +++ b/subworkflows/local/qcfastq_nanoplot_fastqc.nf @@ -2,11 +2,8 @@ * FastQ QC with NanoPlot and fastqc */ -params.nanoplot_fastq_options = [:] -params.fastqc_options = [:] - -include { NANOPLOT } from '../../modules/nf-core/modules/nanoplot/main' addParams( options: params.nanoplot_fastq_options ) -include { FASTQC } from '../../modules/nf-core/modules/fastqc/main' addParams( options: params.fastqc_options ) +include { NANOPLOT } from '../../modules/local/nanoplot' +include { FASTQC } from '../../modules/nf-core/modules/fastqc/main' workflow QCFASTQ_NANOPLOT_FASTQC { take: diff --git a/subworkflows/local/quantify_stringtie_featurecounts.nf b/subworkflows/local/quantify_stringtie_featurecounts.nf index bab6e6b1..c5055167 100644 --- a/subworkflows/local/quantify_stringtie_featurecounts.nf +++ b/subworkflows/local/quantify_stringtie_featurecounts.nf @@ -2,12 +2,9 @@ * Transcript Discovery and Quantification with StringTie2 and FeatureCounts */ -params.stringtie2_options = [:] -params.featurecounts_options = [:] - -include { STRINGTIE2 } from '../../modules/local/stringtie2' addParams( options: params.stringtie2_options ) -include { STRINGTIE_MERGE } from '../../modules/nf-core/modules/stringtie/merge/main' addParams( options: params.stringtie2_options ) -include { SUBREAD_FEATURECOUNTS } from '../../modules/local/subread_featurecounts' addParams( options: params.featurecounts_options ) +include { STRINGTIE2 } from '../../modules/local/stringtie2' +include { STRINGTIE_MERGE } from '../../modules/nf-core/modules/stringtie/merge/main' +include { SUBREAD_FEATURECOUNTS } from '../../modules/local/subread_featurecounts' workflow QUANTIFY_STRINGTIE_FEATURECOUNTS { take: diff --git a/subworkflows/local/sv_sniffles.nf b/subworkflows/local/sv_sniffles.nf deleted file mode 100644 index 9f29cb45..00000000 --- a/subworkflows/local/sv_sniffles.nf +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Structural variant calling with SNIFFLES - */ - -params.sniffles_sv_options = [:] - -include { SNIFFLES } from '../../modules/local/sniffles' addParams( options: params.sniffles_sv_options ) - -workflow SV_SNIFFLES { - take: - ch_view_sortbam - - main: - /* - * Call variants with MEDAKA - */ - SNIFFLES( ch_view_sortbam ) - ch_sv_calls = SNIFFLES.out.sv_calls - - emit: - ch_sv_calls - -} - diff --git a/subworkflows/local/vc_medaka.nf b/subworkflows/local/vc_medaka.nf deleted file mode 100644 index 742f3568..00000000 --- a/subworkflows/local/vc_medaka.nf +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Small variant calling with MEDAKA - */ - -params.medaka_vc_options = [:] - -include { MEDAKA } from '../../modules/local/medaka_variant' addParams( options: params.medaka_vc_options ) - -workflow VC_MEDAKA { - take: - ch_view_sortbam // - ch_index // - - main: - /* - * Split into a different channel for each chromosome - */ - // TODO Add module that cuts chromosomes from reference to use as regions to split variant calling - //SPLIT_CHROM( ch_view_sortbam ) - //.splitCsv() - //.combine( ch_view_sortbam ) // - //.unique() - //.map { it -> [ it[1], it[4], it[5], it[0] ] } // - //.map{ meta, bam, bai, chrom -> - //new_meta = meta.clone() - //new_meta.chrom = chrom - //[new_meta, bam, bai] - //}.set{ch_bam_vc_chrom} - - /* - * Call variants with MEDAKA - */ - MEDAKA( ch_view_sortbam, ch_index ) - ch_variant_calls = MEDAKA.out.variant_calls - - - // TODO Add module to concatenate variant calls back togehter and index - - emit: - ch_variant_calls - -} diff --git a/subworkflows/nf-core/bam_stats_samtools.nf b/subworkflows/nf-core/bam_stats_samtools.nf index 4f1c2f36..5d1c5078 100644 --- a/subworkflows/nf-core/bam_stats_samtools.nf +++ b/subworkflows/nf-core/bam_stats_samtools.nf @@ -2,11 +2,9 @@ * Run SAMtools stats, flagstat and idxstats */ -params.options = [:] - -include { SAMTOOLS_STATS } from '../../modules/nf-core/modules/samtools/stats/main' addParams( options: params.options ) -include { SAMTOOLS_IDXSTATS } from '../../modules/nf-core/modules/samtools/idxstats/main' addParams( options: params.options ) -include { SAMTOOLS_FLAGSTAT } from '../../modules/nf-core/modules/samtools/flagstat/main' addParams( options: params.options ) +include { SAMTOOLS_STATS } from '../../modules/nf-core/modules/samtools/stats/main' +include { SAMTOOLS_IDXSTATS } from '../../modules/nf-core/modules/samtools/idxstats/main' +include { SAMTOOLS_FLAGSTAT } from '../../modules/nf-core/modules/samtools/flagstat/main' workflow BAM_STATS_SAMTOOLS { take: diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index 3a49a654..fb44feca 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -94,58 +94,30 @@ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multi /* -- IMPORT LOCAL MODULES/SUBWORKFLOWS -- */ //////////////////////////////////////////////////// -// Don't overwrite global params.modules, create a copy instead and use that within the main script. -def modules = params.modules.clone() - -def multiqc_options = modules['multiqc'] -multiqc_options.args += params.multiqc_title ? " --title \"$params.multiqc_title\"" : '' -if (params.skip_alignment) { multiqc_options['publish_dir'] = '' } - -def guppy_options = modules['guppy'] -def qcat_options = modules['qcat'] -def nanolyse_options = modules['nanolyse'] -def bambu_options = modules['bambu'] - -include { GET_TEST_DATA } from '../modules/local/get_test_data' addParams( options: [:] ) -include { GET_NANOLYSE_FASTA } from '../modules/local/get_nanolyse_fasta' addParams( options: [:] ) -include { GUPPY } from '../modules/local/guppy' addParams( options: guppy_options ) -include { QCAT } from '../modules/local/qcat' addParams( options: qcat_options ) -include { BAM_RENAME } from '../modules/local/bam_rename' addParams( options: [:] ) -include { BAMBU } from '../modules/local/bambu' addParams( options: bambu_options ) -include { GET_SOFTWARE_VERSIONS } from '../modules/local/get_software_versions' addParams( options: [publish_files : ['csv':'']] ) -include { MULTIQC } from '../modules/local/multiqc' addParams( options: multiqc_options ) +include { GET_TEST_DATA } from '../modules/local/get_test_data' +include { GET_NANOLYSE_FASTA } from '../modules/local/get_nanolyse_fasta' +include { GUPPY } from '../modules/local/guppy' +include { QCAT } from '../modules/local/qcat' +include { BAM_RENAME } from '../modules/local/bam_rename' +include { BAMBU } from '../modules/local/bambu' +include { MULTIQC } from '../modules/local/multiqc' /* * SUBWORKFLOW: Consisting of a mix of local and nf-core/modules */ -def pycoqc_options = modules['pycoqc'] -def nanoplot_options = modules['nanoplot'] -def fastqc_options = modules['fastqc'] -def genome_options = modules['get_chrom_size'] -def graphmap2_index_options = modules['graphmap2_index'] -def graphmap2_align_options = modules['graphmap2_align'] -def minimap2_index_options = modules['minimap2_index'] -def minimap2_align_options = modules['minimap2_align'] -def medaka_vc_options = modules['medaka_vc'] -def sniffles_sv_options = modules['sniffles_sv'] -def samtools_sort_options = modules['samtools_sort'] -def bigwig_options = modules['ucsc_bedgraphtobigwig'] -def bigbed_options = modules['ucsc_bed12tobigbed'] -def stringtie2_options = modules['stringtie2'] -def featurecounts_options = modules['subread_featurecounts'] -def deseq2_options = modules['deseq2'] -def dexseq_options = modules['dexseq'] - -include { INPUT_CHECK } from '../subworkflows/local/input_check' addParams( options: [:] ) -include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome' addParams( genome_options: genome_options ) -include { ALIGN_GRAPHMAP2 } from '../subworkflows/local/align_graphmap2' addParams( index_options: graphmap2_index_options, align_options: graphmap2_align_options ) -include { ALIGN_MINIMAP2 } from '../subworkflows/local/align_minimap2' addParams( index_options: minimap2_index_options, align_options: minimap2_align_options ) -include { BAM_SORT_INDEX_SAMTOOLS } from '../subworkflows/local/bam_sort_index_samtools' addParams( samtools_options: samtools_sort_options ) -include { DNA_VARIANT_CALLING } from '../subworkflows/local/dna_variant_calling' addParams( medaka_vc_options: medaka_vc_options, sniffles_sv_options: sniffles_sv_options) -include { BEDTOOLS_UCSC_BIGWIG } from '../subworkflows/local/bedtools_ucsc_bigwig' addParams( bigwig_options: bigwig_options ) -include { BEDTOOLS_UCSC_BIGBED } from '../subworkflows/local/bedtools_ucsc_bigbed' addParams( bigbed_options: bigbed_options ) -include { QUANTIFY_STRINGTIE_FEATURECOUNTS } from '../subworkflows/local/quantify_stringtie_featurecounts' addParams( stringtie2_options: stringtie2_options, featurecounts_options: featurecounts_options ) -include { DIFFERENTIAL_DESEQ2_DEXSEQ } from '../subworkflows/local/differential_deseq2_dexseq' addParams( deseq2_options: deseq2_options, dexseq_options: dexseq_options ) + +include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome' +include { QCBASECALL_PYCOQC_NANOPLOT } from '../subworkflows/local/qcbasecall_pycoqc_nanoplot' +include { QCFASTQ_NANOPLOT_FASTQC } from '../subworkflows/local/qcfastq_nanoplot_fastqc' +include { ALIGN_GRAPHMAP2 } from '../subworkflows/local/align_graphmap2' +include { ALIGN_MINIMAP2 } from '../subworkflows/local/align_minimap2' +include { BAM_SORT_INDEX_SAMTOOLS } from '../subworkflows/local/bam_sort_index_samtools' +include { DNA_VARIANT_CALLING } from '../subworkflows/local/dna_variant_calling' +include { BEDTOOLS_UCSC_BIGWIG } from '../subworkflows/local/bedtools_ucsc_bigwig' +include { BEDTOOLS_UCSC_BIGBED } from '../subworkflows/local/bedtools_ucsc_bigbed' +include { QUANTIFY_STRINGTIE_FEATURECOUNTS } from '../subworkflows/local/quantify_stringtie_featurecounts' +include { DIFFERENTIAL_DESEQ2_DEXSEQ } from '../subworkflows/local/differential_deseq2_dexseq' //////////////////////////////////////////////////// /* -- IMPORT NF-CORE MODULES/SUBWORKFLOWS -- */ @@ -154,14 +126,12 @@ include { DIFFERENTIAL_DESEQ2_DEXSEQ } from '../subworkflows/local/differe /* * MODULE: Installed directly from nf-core/modules */ -include { NANOLYSE } from '../modules/nf-core/modules/nanolyse/main' addParams( options: nanolyse_options ) -include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' addParams( options: [publish_files : ['_versions.yml':'']] ) +include { NANOLYSE } from '../modules/nf-core/modules/nanolyse/main' +include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' /* * SUBWORKFLOW: Consisting entirely of nf-core/modules (BAM_SORT_SAMTOOLS & BAM_STAT_SAMTOOLS are within two local subworkflows) */ -include { QCBASECALL_PYCOQC_NANOPLOT } from '../subworkflows/nf-core/qcbasecall_pycoqc_nanoplot' addParams( pycoqc_options: pycoqc_options, nanoplot_options: nanoplot_options ) -include { QCFASTQ_NANOPLOT_FASTQC } from '../subworkflows/nf-core/qcfastq_nanoplot_fastqc' addParams( nanoplot_options: nanoplot_options, fastqc_options: fastqc_options ) //////////////////////////////////////////////////// /* -- RUN MAIN WORKFLOW -- */ @@ -175,6 +145,9 @@ workflow NANOSEQ{ /* * SUBWORKFLOW: Read in samplesheet, validate and stage input files */ + left = Channel.from(['X', 1], ['Y', 2], ['Z', 3], ['P', 7]) + right= Channel.from(['Z', 6], ['Y', 5], ['X', 4]) + left.join(right).view() INPUT_CHECK ( ch_input ) .set { ch_sample } From 33325c5dab07c49a4d2cd34366111ec8f19c3cf9 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Fri, 17 Dec 2021 09:45:35 +0800 Subject: [PATCH 15/43] minor fixes --- .gitattributes | 3 +++ conf/modules.config | 4 ++-- lib/NfcoreSchema.groovy | 27 ++++++++++++++++++++------- lib/NfcoreTemplate.groovy | 31 ++++++++++--------------------- modules.json | 6 ------ 5 files changed, 35 insertions(+), 36 deletions(-) diff --git a/.gitattributes b/.gitattributes index 7fe55006..1ff194da 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,4 @@ *.config linguist-language=nextflow +modules/nf-core/** linguist-generated +subworkflows/nf-core/** linguist-generated + diff --git a/conf/modules.config b/conf/modules.config index e969133a..3f0e1632 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -258,7 +258,7 @@ if (!params.skip_quantification) { ] } } - if (!params.differential_analysis) { + if (!params.skip_differential_analysis) { process { withName: DESEQ2 { publishDir = [ @@ -294,7 +294,7 @@ if (!params.skip_quantification) { ] } } - if (!params.differential_analysis) { + if (!params.skip_differential_analysis) { process { withName: DESEQ2 { publishDir = [ diff --git a/lib/NfcoreSchema.groovy b/lib/NfcoreSchema.groovy index 8d6920dd..ed96f4fe 100755 --- a/lib/NfcoreSchema.groovy +++ b/lib/NfcoreSchema.groovy @@ -105,9 +105,13 @@ class NfcoreSchema { // Collect expected parameters from the schema def expectedParams = [] + def enums = [:] for (group in schemaParams) { for (p in group.value['properties']) { expectedParams.push(p.key) + if (group.value['properties'][p.key].containsKey('enum')) { + enums[p.key] = group.value['properties'][p.key]['enum'] + } } } @@ -155,7 +159,7 @@ class NfcoreSchema { println '' log.error 'ERROR: Validation of pipeline parameters failed!' JSONObject exceptionJSON = e.toJSON() - printExceptions(exceptionJSON, params_json, log) + printExceptions(exceptionJSON, params_json, log, enums) println '' has_error = true } @@ -202,7 +206,7 @@ class NfcoreSchema { } def type = '[' + group_params.get(param).type + ']' def description = group_params.get(param).description - def defaultValue = group_params.get(param).default ? " [default: " + group_params.get(param).default.toString() + "]" : '' + def defaultValue = group_params.get(param).default != null ? " [default: " + group_params.get(param).default.toString() + "]" : '' def description_default = description + colors.dim + defaultValue + colors.reset // Wrap long description texts // Loosely based on https://dzone.com/articles/groovy-plain-text-word-wrap @@ -260,13 +264,12 @@ class NfcoreSchema { // Get pipeline parameters defined in JSON Schema def Map params_summary = [:] - def blacklist = ['hostnames'] def params_map = paramsLoad(getSchemaPath(workflow, schema_filename=schema_filename)) for (group in params_map.keySet()) { def sub_params = new LinkedHashMap() def group_params = params_map.get(group) // This gets the parameters of that particular group for (param in group_params.keySet()) { - if (params.containsKey(param) && !blacklist.contains(param)) { + if (params.containsKey(param)) { def params_value = params.get(param) def schema_value = group_params.get(param).default def param_type = group_params.get(param).type @@ -330,7 +333,7 @@ class NfcoreSchema { // // Loop over nested exceptions and print the causingException // - private static void printExceptions(ex_json, params_json, log) { + private static void printExceptions(ex_json, params_json, log, enums, limit=5) { def causingExceptions = ex_json['causingExceptions'] if (causingExceptions.length() == 0) { def m = ex_json['message'] =~ /required key \[([^\]]+)\] not found/ @@ -346,11 +349,20 @@ class NfcoreSchema { else { def param = ex_json['pointerToViolation'] - ~/^#\// def param_val = params_json[param].toString() - log.error "* --${param}: ${ex_json['message']} (${param_val})" + if (enums.containsKey(param)) { + def error_msg = "* --${param}: '${param_val}' is not a valid choice (Available choices" + if (enums[param].size() > limit) { + log.error "${error_msg} (${limit} of ${enums[param].size()}): ${enums[param][0..limit-1].join(', ')}, ... )" + } else { + log.error "${error_msg}: ${enums[param].join(', ')})" + } + } else { + log.error "* --${param}: ${ex_json['message']} (${param_val})" + } } } for (ex in causingExceptions) { - printExceptions(ex, params_json, log) + printExceptions(ex, params_json, log, enums) } } @@ -515,3 +527,4 @@ class NfcoreSchema { return max_chars } } + diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 44551e0a..4b90eb9b 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -19,27 +19,16 @@ class NfcoreTemplate { } // - // Check params.hostnames + // Warn if a -profile or Nextflow config has not been provided to run the pipeline // - public static void hostName(workflow, params, log) { - Map colors = logColours(params.monochrome_logs) - if (params.hostnames) { - try { - def hostname = "hostname".execute().text.trim() - params.hostnames.each { prof, hnames -> - hnames.each { hname -> - if (hostname.contains(hname) && !workflow.profile.contains(prof)) { - log.info "=${colors.yellow}====================================================${colors.reset}=\n" + - "${colors.yellow}WARN: You are running with `-profile $workflow.profile`\n" + - " but your machine hostname is ${colors.white}'$hostname'${colors.reset}.\n" + - " ${colors.yellow_bold}Please use `-profile $prof${colors.reset}`\n" + - "=${colors.yellow}====================================================${colors.reset}=" - } - } - } - } catch (Exception e) { - log.warn "[$workflow.manifest.name] Could not determine 'hostname' - skipping check. Reason: ${e.message}." - } + public static void checkConfigProvided(workflow, log) { + if (workflow.profile == 'standard' && workflow.configFiles.size() <= 1) { + log.warn "[$workflow.manifest.name] You are attempting to run the pipeline without any custom configuration!\n\n" + + "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" + + " (1) Using an existing pipeline profile e.g. `-profile docker` or `-profile singularity`\n" + + " (2) Using an existing nf-core/configs for your Institution e.g. `-profile crick` or `-profile uppmax`\n" + + " (3) Using your own local custom config e.g. `-c /path/to/your/custom.config`\n\n" + + "Please refer to the quick start section and usage docs for the pipeline.\n " } } @@ -168,7 +157,6 @@ class NfcoreTemplate { log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed successfully, but with errored process(es) ${colors.reset}-" } } else { - hostName(workflow, params, log) log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-" } } @@ -268,3 +256,4 @@ class NfcoreTemplate { ) } } + diff --git a/modules.json b/modules.json index c294a50b..77d36baa 100644 --- a/modules.json +++ b/modules.json @@ -12,9 +12,6 @@ "nanolyse": { "git_sha": "3aacd46da2b221ed47aaa05c413a828538d2c2ae" }, - "nanoplot": { - "git_sha": "3aacd46da2b221ed47aaa05c413a828538d2c2ae" - }, "pycoqc": { "git_sha": "49da8642876ae4d91128168cd0db4f1c858d7792" }, @@ -35,9 +32,6 @@ }, "stringtie/merge": { "git_sha": "3aacd46da2b221ed47aaa05c413a828538d2c2ae" - }, - "multiqc": { - "git_sha": "97505c6b4a7ad5729faf2ef8b68eff60630c0930" } } } From 5fc96c3d3a97bd83a3c6c53ec27842d0b6356cf4 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Fri, 17 Dec 2021 09:50:27 +0800 Subject: [PATCH 16/43] revert to original lib --- .gitattributes | 1 - lib/NfcoreSchema.groovy | 27 +++++++-------------------- lib/NfcoreTemplate.groovy | 31 +++++++++++++++++++++---------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/.gitattributes b/.gitattributes index 1ff194da..050bb120 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,3 @@ *.config linguist-language=nextflow modules/nf-core/** linguist-generated subworkflows/nf-core/** linguist-generated - diff --git a/lib/NfcoreSchema.groovy b/lib/NfcoreSchema.groovy index ed96f4fe..8d6920dd 100755 --- a/lib/NfcoreSchema.groovy +++ b/lib/NfcoreSchema.groovy @@ -105,13 +105,9 @@ class NfcoreSchema { // Collect expected parameters from the schema def expectedParams = [] - def enums = [:] for (group in schemaParams) { for (p in group.value['properties']) { expectedParams.push(p.key) - if (group.value['properties'][p.key].containsKey('enum')) { - enums[p.key] = group.value['properties'][p.key]['enum'] - } } } @@ -159,7 +155,7 @@ class NfcoreSchema { println '' log.error 'ERROR: Validation of pipeline parameters failed!' JSONObject exceptionJSON = e.toJSON() - printExceptions(exceptionJSON, params_json, log, enums) + printExceptions(exceptionJSON, params_json, log) println '' has_error = true } @@ -206,7 +202,7 @@ class NfcoreSchema { } def type = '[' + group_params.get(param).type + ']' def description = group_params.get(param).description - def defaultValue = group_params.get(param).default != null ? " [default: " + group_params.get(param).default.toString() + "]" : '' + def defaultValue = group_params.get(param).default ? " [default: " + group_params.get(param).default.toString() + "]" : '' def description_default = description + colors.dim + defaultValue + colors.reset // Wrap long description texts // Loosely based on https://dzone.com/articles/groovy-plain-text-word-wrap @@ -264,12 +260,13 @@ class NfcoreSchema { // Get pipeline parameters defined in JSON Schema def Map params_summary = [:] + def blacklist = ['hostnames'] def params_map = paramsLoad(getSchemaPath(workflow, schema_filename=schema_filename)) for (group in params_map.keySet()) { def sub_params = new LinkedHashMap() def group_params = params_map.get(group) // This gets the parameters of that particular group for (param in group_params.keySet()) { - if (params.containsKey(param)) { + if (params.containsKey(param) && !blacklist.contains(param)) { def params_value = params.get(param) def schema_value = group_params.get(param).default def param_type = group_params.get(param).type @@ -333,7 +330,7 @@ class NfcoreSchema { // // Loop over nested exceptions and print the causingException // - private static void printExceptions(ex_json, params_json, log, enums, limit=5) { + private static void printExceptions(ex_json, params_json, log) { def causingExceptions = ex_json['causingExceptions'] if (causingExceptions.length() == 0) { def m = ex_json['message'] =~ /required key \[([^\]]+)\] not found/ @@ -349,20 +346,11 @@ class NfcoreSchema { else { def param = ex_json['pointerToViolation'] - ~/^#\// def param_val = params_json[param].toString() - if (enums.containsKey(param)) { - def error_msg = "* --${param}: '${param_val}' is not a valid choice (Available choices" - if (enums[param].size() > limit) { - log.error "${error_msg} (${limit} of ${enums[param].size()}): ${enums[param][0..limit-1].join(', ')}, ... )" - } else { - log.error "${error_msg}: ${enums[param].join(', ')})" - } - } else { - log.error "* --${param}: ${ex_json['message']} (${param_val})" - } + log.error "* --${param}: ${ex_json['message']} (${param_val})" } } for (ex in causingExceptions) { - printExceptions(ex, params_json, log, enums) + printExceptions(ex, params_json, log) } } @@ -527,4 +515,3 @@ class NfcoreSchema { return max_chars } } - diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 4b90eb9b..44551e0a 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -19,16 +19,27 @@ class NfcoreTemplate { } // - // Warn if a -profile or Nextflow config has not been provided to run the pipeline + // Check params.hostnames // - public static void checkConfigProvided(workflow, log) { - if (workflow.profile == 'standard' && workflow.configFiles.size() <= 1) { - log.warn "[$workflow.manifest.name] You are attempting to run the pipeline without any custom configuration!\n\n" + - "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" + - " (1) Using an existing pipeline profile e.g. `-profile docker` or `-profile singularity`\n" + - " (2) Using an existing nf-core/configs for your Institution e.g. `-profile crick` or `-profile uppmax`\n" + - " (3) Using your own local custom config e.g. `-c /path/to/your/custom.config`\n\n" + - "Please refer to the quick start section and usage docs for the pipeline.\n " + public static void hostName(workflow, params, log) { + Map colors = logColours(params.monochrome_logs) + if (params.hostnames) { + try { + def hostname = "hostname".execute().text.trim() + params.hostnames.each { prof, hnames -> + hnames.each { hname -> + if (hostname.contains(hname) && !workflow.profile.contains(prof)) { + log.info "=${colors.yellow}====================================================${colors.reset}=\n" + + "${colors.yellow}WARN: You are running with `-profile $workflow.profile`\n" + + " but your machine hostname is ${colors.white}'$hostname'${colors.reset}.\n" + + " ${colors.yellow_bold}Please use `-profile $prof${colors.reset}`\n" + + "=${colors.yellow}====================================================${colors.reset}=" + } + } + } + } catch (Exception e) { + log.warn "[$workflow.manifest.name] Could not determine 'hostname' - skipping check. Reason: ${e.message}." + } } } @@ -157,6 +168,7 @@ class NfcoreTemplate { log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed successfully, but with errored process(es) ${colors.reset}-" } } else { + hostName(workflow, params, log) log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-" } } @@ -256,4 +268,3 @@ class NfcoreTemplate { ) } } - From 66ce1952e6f94f2695ab9493730a56a87c8e871d Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Fri, 17 Dec 2021 11:01:34 +0800 Subject: [PATCH 17/43] update some lib/groovys --- lib/Headers.groovy | 1 - lib/NfcoreSchema.groovy | 26 +++-- lib/Schema.groovy | 228 ---------------------------------------- lib/Utils.groovy | 8 +- workflows/.Rhistory | 0 workflows/nanoseq.nf | 6 +- 6 files changed, 23 insertions(+), 246 deletions(-) delete mode 100644 lib/Schema.groovy delete mode 100644 workflows/.Rhistory diff --git a/lib/Headers.groovy b/lib/Headers.groovy index 036611d3..15d1d388 100644 --- a/lib/Headers.groovy +++ b/lib/Headers.groovy @@ -41,4 +41,3 @@ class Headers { ) } } - diff --git a/lib/NfcoreSchema.groovy b/lib/NfcoreSchema.groovy index 8d6920dd..40ab65f2 100755 --- a/lib/NfcoreSchema.groovy +++ b/lib/NfcoreSchema.groovy @@ -105,9 +105,13 @@ class NfcoreSchema { // Collect expected parameters from the schema def expectedParams = [] + def enums = [:] for (group in schemaParams) { for (p in group.value['properties']) { expectedParams.push(p.key) + if (group.value['properties'][p.key].containsKey('enum')) { + enums[p.key] = group.value['properties'][p.key]['enum'] + } } } @@ -155,7 +159,7 @@ class NfcoreSchema { println '' log.error 'ERROR: Validation of pipeline parameters failed!' JSONObject exceptionJSON = e.toJSON() - printExceptions(exceptionJSON, params_json, log) + printExceptions(exceptionJSON, params_json, log, enums) println '' has_error = true } @@ -202,7 +206,7 @@ class NfcoreSchema { } def type = '[' + group_params.get(param).type + ']' def description = group_params.get(param).description - def defaultValue = group_params.get(param).default ? " [default: " + group_params.get(param).default.toString() + "]" : '' + def defaultValue = group_params.get(param).default != null ? " [default: " + group_params.get(param).default.toString() + "]" : '' def description_default = description + colors.dim + defaultValue + colors.reset // Wrap long description texts // Loosely based on https://dzone.com/articles/groovy-plain-text-word-wrap @@ -260,13 +264,12 @@ class NfcoreSchema { // Get pipeline parameters defined in JSON Schema def Map params_summary = [:] - def blacklist = ['hostnames'] def params_map = paramsLoad(getSchemaPath(workflow, schema_filename=schema_filename)) for (group in params_map.keySet()) { def sub_params = new LinkedHashMap() def group_params = params_map.get(group) // This gets the parameters of that particular group for (param in group_params.keySet()) { - if (params.containsKey(param) && !blacklist.contains(param)) { + if (params.containsKey(param)) { def params_value = params.get(param) def schema_value = group_params.get(param).default def param_type = group_params.get(param).type @@ -330,7 +333,7 @@ class NfcoreSchema { // // Loop over nested exceptions and print the causingException // - private static void printExceptions(ex_json, params_json, log) { + private static void printExceptions(ex_json, params_json, log, enums, limit=5) { def causingExceptions = ex_json['causingExceptions'] if (causingExceptions.length() == 0) { def m = ex_json['message'] =~ /required key \[([^\]]+)\] not found/ @@ -346,11 +349,20 @@ class NfcoreSchema { else { def param = ex_json['pointerToViolation'] - ~/^#\// def param_val = params_json[param].toString() - log.error "* --${param}: ${ex_json['message']} (${param_val})" + if (enums.containsKey(param)) { + def error_msg = "* --${param}: '${param_val}' is not a valid choice (Available choices" + if (enums[param].size() > limit) { + log.error "${error_msg} (${limit} of ${enums[param].size()}): ${enums[param][0..limit-1].join(', ')}, ... )" + } else { + log.error "${error_msg}: ${enums[param].join(', ')})" + } + } else { + log.error "* --${param}: ${ex_json['message']} (${param_val})" + } } } for (ex in causingExceptions) { - printExceptions(ex, params_json, log) + printExceptions(ex, params_json, log, enums) } } diff --git a/lib/Schema.groovy b/lib/Schema.groovy deleted file mode 100644 index 5105c7ba..00000000 --- a/lib/Schema.groovy +++ /dev/null @@ -1,228 +0,0 @@ -/* - * This file holds several functions used to perform JSON parameter validation, help and summary rendering for the nf-core pipeline template. - */ - -import groovy.json.JsonSlurper - -class Schema { - /* - * This method tries to read a JSON params file - */ - private static LinkedHashMap params_load(String json_schema) { - def params_map = new LinkedHashMap() - try { - params_map = params_read(json_schema) - } catch (Exception e) { - println "Could not read parameters settings from JSON. $e" - params_map = new LinkedHashMap() - } - return params_map - } - - /* - Method to actually read in JSON file using Groovy. - Group (as Key), values are all parameters - - Parameter1 as Key, Description as Value - - Parameter2 as Key, Description as Value - .... - Group - - - */ - private static LinkedHashMap params_read(String json_schema) throws Exception { - def json = new File(json_schema).text - def Map json_params = (Map) new JsonSlurper().parseText(json).get('definitions') - /* Tree looks like this in nf-core schema - * definitions <- this is what the first get('definitions') gets us - group 1 - title - description - properties - parameter 1 - type - description - parameter 2 - type - description - group 2 - title - description - properties - parameter 1 - type - description - */ - def params_map = new LinkedHashMap() - json_params.each { key, val -> - def Map group = json_params."$key".properties // Gets the property object of the group - def title = json_params."$key".title - def sub_params = new LinkedHashMap() - group.each { innerkey, value -> - sub_params.put(innerkey, value) - } - params_map.put(title, sub_params) - } - return params_map - } - - /* - * Get maximum number of characters across all parameter names - */ - private static Integer params_max_chars(params_map) { - Integer max_chars = 0 - for (group in params_map.keySet()) { - def group_params = params_map.get(group) // This gets the parameters of that particular group - for (param in group_params.keySet()) { - if (param.size() > max_chars) { - max_chars = param.size() - } - } - } - return max_chars - } - - /* - * Beautify parameters for --help - */ - private static String params_help(workflow, params, json_schema, command) { - String output = Headers.nf_core(workflow, params.monochrome_logs) + "\n" - output += "Typical pipeline command:\n\n" - output += " ${command}\n\n" - def params_map = params_load(json_schema) - def max_chars = params_max_chars(params_map) + 1 - for (group in params_map.keySet()) { - output += group + "\n" - def group_params = params_map.get(group) // This gets the parameters of that particular group - for (param in group_params.keySet()) { - def type = "[" + group_params.get(param).type + "]" - def description = group_params.get(param).description - output += " \u001B[1m--" + param.padRight(max_chars) + "\u001B[1m" + type.padRight(10) + description + "\n" - } - output += "\n" - } - output += Headers.dashed_line(params.monochrome_logs) - output += "\n\n" + Checks.citation(workflow) - output += "\n\n" + Headers.dashed_line(params.monochrome_logs) - return output - } - - /* - * Groovy Map summarising parameters/workflow options used by the pipeline - */ - private static LinkedHashMap params_summary_map(workflow, params, json_schema) { - // Get a selection of core Nextflow workflow options - def Map workflow_summary = [:] - if (workflow.revision) { - workflow_summary['revision'] = workflow.revision - } - workflow_summary['runName'] = workflow.runName - if (workflow.containerEngine) { - workflow_summary['containerEngine'] = "$workflow.containerEngine" - } - if (workflow.container) { - workflow_summary['container'] = "$workflow.container" - } - workflow_summary['launchDir'] = workflow.launchDir - workflow_summary['workDir'] = workflow.workDir - workflow_summary['projectDir'] = workflow.projectDir - workflow_summary['userName'] = workflow.userName - workflow_summary['profile'] = workflow.profile - workflow_summary['configFiles'] = workflow.configFiles.join(', ') - - // Get pipeline parameters defined in JSON Schema - def Map params_summary = [:] - def blacklist = ['hostnames'] - def params_map = params_load(json_schema) - for (group in params_map.keySet()) { - def sub_params = new LinkedHashMap() - def group_params = params_map.get(group) // This gets the parameters of that particular group - for (param in group_params.keySet()) { - if (params.containsKey(param) && !blacklist.contains(param)) { - def params_value = params.get(param) - def schema_value = group_params.get(param).default - def param_type = group_params.get(param).type - if (schema_value == null) { - if (param_type == 'boolean') { - schema_value = false - } - if (param_type == 'string') { - schema_value = '' - } - if (param_type == 'integer') { - schema_value = 0 - } - } else { - if (param_type == 'string') { - if (schema_value.contains('$projectDir') || schema_value.contains('${projectDir}')) { - def sub_string = schema_value.replace('\$projectDir','') - sub_string = sub_string.replace('\${projectDir}','') - if (params_value.contains(sub_string)) { - schema_value = params_value - } - } - if (schema_value.contains('$params.outdir') || schema_value.contains('${params.outdir}')) { - def sub_string = schema_value.replace('\$params.outdir','') - sub_string = sub_string.replace('\${params.outdir}','') - if ("${params.outdir}${sub_string}" == params_value) { - schema_value = params_value - } - } - } - } - - if (params_value != schema_value) { - sub_params.put("$param", params_value) - } - } - } - params_summary.put(group, sub_params) - } - return [ 'Core Nextflow options' : workflow_summary ] << params_summary - } - - /* - * Beautify parameters for summary and return as string - */ - private static String params_summary_log(workflow, params, json_schema) { - String output = Headers.nf_core(workflow, params.monochrome_logs) + "\n" - def params_map = params_summary_map(workflow, params, json_schema) - def max_chars = params_max_chars(params_map) - for (group in params_map.keySet()) { - def group_params = params_map.get(group) // This gets the parameters of that particular group - if (group_params) { - output += group + "\n" - for (param in group_params.keySet()) { - output += " \u001B[1m" + param.padRight(max_chars) + ": \u001B[1m" + group_params.get(param) + "\n" - } - output += "\n" - } - } - output += Headers.dashed_line(params.monochrome_logs) - output += "\n\n" + Checks.citation(workflow) - output += "\n\n" + Headers.dashed_line(params.monochrome_logs) - return output - } - - static String params_summary_multiqc(workflow, summary) { - String summary_section = '' - for (group in summary.keySet()) { - def group_params = summary.get(group) // This gets the parameters of that particular group - if (group_params) { - summary_section += "

$group

\n" - summary_section += "
\n" - for (param in group_params.keySet()) { - summary_section += "
$param
${group_params.get(param) ?: 'N/A'}
\n" - } - summary_section += "
\n" - } - } - - String yaml_file_text = "id: '${workflow.manifest.name.replace('/','-')}-summary'\n" - yaml_file_text += "description: ' - this information is collected when the pipeline is started.'\n" - yaml_file_text += "section_name: '${workflow.manifest.name} Workflow Summary'\n" - yaml_file_text += "section_href: 'https://github.com/${workflow.manifest.name}'\n" - yaml_file_text += "plot_type: 'html'\n" - yaml_file_text += "data: |\n" - yaml_file_text += "${summary_section}" - return yaml_file_text - } -} diff --git a/lib/Utils.groovy b/lib/Utils.groovy index 18173e98..242ea4db 100755 --- a/lib/Utils.groovy +++ b/lib/Utils.groovy @@ -37,11 +37,5 @@ class Utils { "===================================================================================" } } - - // - // Join module args with appropriate spacing - // - public static String joinModuleArgs(args_list) { - return ' ' + args_list.join(' ') - } } + diff --git a/workflows/.Rhistory b/workflows/.Rhistory deleted file mode 100644 index e69de29b..00000000 diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index fb44feca..0a2b79ed 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -2,7 +2,7 @@ /* -- LOCAL PARAMETER VALUES -- */ //////////////////////////////////////////////////// -params.summary_params = [:] +def summary_params = NfcoreSchema.paramsSummaryMap(workflow, params) //////////////////////////////////////////////////// /* -- VALIDATE INPUTS -- */ @@ -410,8 +410,8 @@ workflow NANOSEQ{ ) if (!params.skip_multiqc){ - workflow_summary = Schema.params_summary_multiqc(workflow, params.summary_params) - ch_workflow_summary = Channel.value(workflow_summary).collectFile(name: 'workflow_summary_mqc.yaml') + workflow_summary = WorkflowNanoseq.paramsSummaryMultiqc(workflow, summary_params) + ch_workflow_summary = Channel.value(workflow_summary) /* * MODULE: MultiQC From 708bc5b855f3ae0048bcaae4d2e18ea93656b500 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Fri, 17 Dec 2021 11:16:20 +0800 Subject: [PATCH 18/43] sort out the rest of the lib/groovys --- lib/Checks.groovy | 109 -------------------------- lib/Completion.groovy | 156 -------------------------------------- lib/Headers.groovy | 43 ----------- lib/NfcoreTemplate.groovy | 30 +++----- lib/WorkflowMain.groovy | 6 +- workflows/nanoseq.nf | 8 +- 6 files changed, 18 insertions(+), 334 deletions(-) delete mode 100644 lib/Checks.groovy delete mode 100644 lib/Completion.groovy delete mode 100644 lib/Headers.groovy diff --git a/lib/Checks.groovy b/lib/Checks.groovy deleted file mode 100644 index 4a4f0ca8..00000000 --- a/lib/Checks.groovy +++ /dev/null @@ -1,109 +0,0 @@ -import org.yaml.snakeyaml.Yaml - -/* - * This file holds several functions used to perform standard checks for the nf-core pipeline template. - */ - -class Checks { - - static void check_conda_channels(log) { - Yaml parser = new Yaml() - def channels = [] - try { - def config = parser.load("conda config --show channels".execute().text) - channels = config.channels - } catch(NullPointerException | IOException e) { - log.warn "Could not verify conda channel configuration." - return - } - - // Check that all channels are present - def required_channels = ['conda-forge', 'bioconda', 'defaults'] - def conda_check_failed = !required_channels.every { ch -> ch in channels } - - // Check that they are in the right order - conda_check_failed |= !(channels.indexOf('conda-forge') < channels.indexOf('bioconda')) - conda_check_failed |= !(channels.indexOf('bioconda') < channels.indexOf('defaults')) - - if (conda_check_failed) { - log.warn "=============================================================================\n" + - " There is a problem with your Conda configuration!\n\n" + - " You will need to set-up the conda-forge and bioconda channels correctly.\n" + - " Please refer to https://bioconda.github.io/user/install.html#set-up-channels\n" + - " NB: The order of the channels matters!\n" + - "===================================================================================" - } - } - - static void aws_batch(workflow, params) { - if (workflow.profile.contains('awsbatch')) { - assert (params.awsqueue && params.awsregion) : "Specify correct --awsqueue and --awsregion parameters on AWSBatch!" - // Check outdir paths to be S3 buckets if running on AWSBatch - // related: https://github.com/nextflow-io/nextflow/issues/813 - assert params.outdir.startsWith('s3:') : "Outdir not on S3 - specify S3 Bucket to run on AWSBatch!" - // Prevent trace files to be stored on S3 since S3 does not support rolling files. - assert !params.tracedir.startsWith('s3:') : "Specify a local tracedir or run without trace! S3 cannot be used for tracefiles." - } - } - - static void hostname(workflow, params, log) { - Map colors = Headers.log_colours(params.monochrome_logs) - if (params.hostnames) { - def hostname = "hostname".execute().text.trim() - params.hostnames.each { prof, hnames -> - hnames.each { hname -> - if (hostname.contains(hname) && !workflow.profile.contains(prof)) { - log.info "=${colors.yellow}====================================================${colors.reset}=\n" + - "${colors.yellow}WARN: You are running with `-profile $workflow.profile`\n" + - " but your machine hostname is ${colors.white}'$hostname'${colors.reset}.\n" + - " ${colors.yellow_bold}Please use `-profile $prof${colors.reset}`\n" + - "=${colors.yellow}====================================================${colors.reset}=" - } - } - } - } - } - - // Citation string - private static String citation(workflow) { - return "If you use ${workflow.manifest.name} for your analysis please cite:\n\n" + - "* The pipeline\n" + - " https://doi.org/10.5281/zenodo.3697959\n\n" + - "* The nf-core framework\n" + - " https://dx.doi.org/10.1038/s41587-020-0439-x\n" + - " https://rdcu.be/b1GjZ\n\n" + - "* Software dependencies\n" + - " https://github.com/${workflow.manifest.name}/blob/master/CITATIONS.md" - } - - // Print a warning if using GRCh38 assembly from igenomes.config - static void ncbi_genome_warn(log) { - log.warn "=============================================================================\n" + - " When using '--genome GRCh38' the assembly is from the NCBI and NOT Ensembl.\n" + - " Auto-activating '--skip_biotype_qc' parameter to circumvent the issue below:\n" + - " https://github.com/nf-core/rnaseq/issues/460.\n\n" + - " If you would like to use the soft-masked Ensembl assembly instead please see:\n" + - " https://github.com/nf-core/rnaseq/issues/159#issuecomment-501184312\n" + - "===================================================================================" - } - - // Print a warning if using a UCSC assembly from igenomes.config - static void ucsc_genome_warn(log) { - log.warn "=============================================================================\n" + - " When using UCSC assemblies the 'gene_biotype' field is absent from the GTF file.\n" + - " Auto-activating '--skip_biotype_qc' parameter to circumvent the issue below:\n" + - " https://github.com/nf-core/rnaseq/issues/460.\n\n" + - " If you would like to use the soft-masked Ensembl assembly instead please see:\n" + - " https://github.com/nf-core/rnaseq/issues/159#issuecomment-501184312\n" + - "===================================================================================" - } - - // Print a warning if --skip_alignment has been provided - static void skip_alignment_warn(log) { - log.warn "=============================================================================\n" + - " '--skip_alignment' parameter has been provided.\n" + - " Skipping alignment, quantification and all downstream QC processes.\n" + - "===================================================================================" - } - -} diff --git a/lib/Completion.groovy b/lib/Completion.groovy deleted file mode 100644 index 1e83fdad..00000000 --- a/lib/Completion.groovy +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Functions to be run on completion of pipeline - */ - -class Completion { - static void email(workflow, params, summary_params, projectDir, log, multiqc_report=[], fail_percent_mapped=[:]) { - - // Set up the e-mail variables - def subject = "[$workflow.manifest.name] Successful: $workflow.runName" - if (fail_percent_mapped.size() > 0) { - subject = "[$workflow.manifest.name] Partially successful (${fail_percent_mapped.size()} skipped): $workflow.runName" - } - if (!workflow.success) { - subject = "[$workflow.manifest.name] FAILED: $workflow.runName" - } - - def summary = [:] - for (group in summary_params.keySet()) { - summary << summary_params[group] - } - - def misc_fields = [:] - misc_fields['Date Started'] = workflow.start - misc_fields['Date Completed'] = workflow.complete - misc_fields['Pipeline script file path'] = workflow.scriptFile - misc_fields['Pipeline script hash ID'] = workflow.scriptId - if (workflow.repository) misc_fields['Pipeline repository Git URL'] = workflow.repository - if (workflow.commitId) misc_fields['Pipeline repository Git Commit'] = workflow.commitId - if (workflow.revision) misc_fields['Pipeline Git branch/tag'] = workflow.revision - misc_fields['Nextflow Version'] = workflow.nextflow.version - misc_fields['Nextflow Build'] = workflow.nextflow.build - misc_fields['Nextflow Compile Timestamp'] = workflow.nextflow.timestamp - - def email_fields = [:] - email_fields['version'] = workflow.manifest.version - email_fields['runName'] = workflow.runName - email_fields['success'] = workflow.success - email_fields['dateComplete'] = workflow.complete - email_fields['duration'] = workflow.duration - email_fields['exitStatus'] = workflow.exitStatus - email_fields['errorMessage'] = (workflow.errorMessage ?: 'None') - email_fields['errorReport'] = (workflow.errorReport ?: 'None') - email_fields['commandLine'] = workflow.commandLine - email_fields['projectDir'] = workflow.projectDir - email_fields['summary'] = summary << misc_fields - email_fields['fail_percent_mapped'] = fail_percent_mapped.keySet() - email_fields['min_mapped_reads'] = params.min_mapped_reads - - // On success try attach the multiqc report - def mqc_report = null - try { - if (workflow.success && !params.skip_multiqc) { - mqc_report = multiqc_report.getVal() - if (mqc_report.getClass() == ArrayList && mqc_report.size() >= 1) { - if (mqc_report.size() > 1) { - log.warn "[$workflow.manifest.name] Found multiple reports from process 'MULTIQC', will use only one" - } - mqc_report = mqc_report[0] - } - } - } catch (all) { - log.warn "[$workflow.manifest.name] Could not attach MultiQC report to summary email" - } - - // Check if we are only sending emails on failure - def email_address = params.email - if (!params.email && params.email_on_fail && !workflow.success) { - email_address = params.email_on_fail - } - - // Render the TXT template - def engine = new groovy.text.GStringTemplateEngine() - def tf = new File("$projectDir/assets/email_template.txt") - def txt_template = engine.createTemplate(tf).make(email_fields) - def email_txt = txt_template.toString() - - // Render the HTML template - def hf = new File("$projectDir/assets/email_template.html") - def html_template = engine.createTemplate(hf).make(email_fields) - def email_html = html_template.toString() - - // Render the sendmail template - def max_multiqc_email_size = params.max_multiqc_email_size as nextflow.util.MemoryUnit - def smail_fields = [ email: email_address, subject: subject, email_txt: email_txt, email_html: email_html, projectDir: "$projectDir", mqcFile: mqc_report, mqcMaxSize: max_multiqc_email_size.toBytes()] - def sf = new File("$projectDir/assets/sendmail_template.txt") - def sendmail_template = engine.createTemplate(sf).make(smail_fields) - def sendmail_html = sendmail_template.toString() - - // Send the HTML e-mail - Map colors = Headers.log_colours(params.monochrome_logs) - if (email_address) { - try { - if (params.plaintext_email) { throw GroovyException('Send plaintext e-mail, not HTML') } - // Try to send HTML e-mail using sendmail - [ 'sendmail', '-t' ].execute() << sendmail_html - log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (sendmail)-" - } catch (all) { - // Catch failures and try with plaintext - def mail_cmd = [ 'mail', '-s', subject, '--content-type=text/html', email_address ] - if ( mqc_report.size() <= max_multiqc_email_size.toBytes() ) { - mail_cmd += [ '-A', mqc_report ] - } - mail_cmd.execute() << email_html - log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (mail)-" - } - } - - // Write summary e-mail HTML to a file - def output_d = new File("${params.outdir}/pipeline_info/") - if (!output_d.exists()) { - output_d.mkdirs() - } - def output_hf = new File(output_d, "pipeline_report.html") - output_hf.withWriter { w -> w << email_html } - def output_tf = new File(output_d, "pipeline_report.txt") - output_tf.withWriter { w -> w << email_txt } - } - - static void summary(workflow, params, log, fail_percent_mapped=[:], pass_percent_mapped=[:]) { - Map colors = Headers.log_colours(params.monochrome_logs) - - if (pass_percent_mapped.size() > 0) { - def idx = 0 - def samp_aln = '' - def total_aln_count = pass_percent_mapped.size() + fail_percent_mapped.size() - for (samp in pass_percent_mapped) { - samp_aln += " ${samp.value}%: ${samp.key}\n" - idx += 1 - if (idx > 5) { - samp_aln += " ..see pipeline reports for full list\n" - break; - } - } - log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} ${pass_percent_mapped.size()}/$total_aln_count samples passed STAR ${params.min_mapped_reads}% mapped threshold:\n${samp_aln}${colors.reset}-" - } - if (fail_percent_mapped.size() > 0) { - def samp_aln = '' - for (samp in fail_percent_mapped) { - samp_aln += " ${samp.value}%: ${samp.key}\n" - } - log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} ${fail_percent_mapped.size()} samples skipped since they failed STAR ${params.min_mapped_reads}% mapped threshold:\n${samp_aln}${colors.reset}-" - } - - if (workflow.success) { - if (workflow.stats.ignoredCount == 0) { - log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Pipeline completed successfully${colors.reset}-" - } else { - log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed successfully, but with errored process(es) ${colors.reset}-" - } - } else { - Checks.hostname(workflow, params, log) - log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-" - } - } -} - diff --git a/lib/Headers.groovy b/lib/Headers.groovy deleted file mode 100644 index 15d1d388..00000000 --- a/lib/Headers.groovy +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file holds several functions used to render the nf-core ANSI header. - */ - -class Headers { - - private static Map log_colours(Boolean monochrome_logs) { - Map colorcodes = [:] - colorcodes['reset'] = monochrome_logs ? '' : "\033[0m" - colorcodes['dim'] = monochrome_logs ? '' : "\033[2m" - colorcodes['black'] = monochrome_logs ? '' : "\033[0;30m" - colorcodes['green'] = monochrome_logs ? '' : "\033[0;32m" - colorcodes['yellow'] = monochrome_logs ? '' : "\033[0;33m" - colorcodes['yellow_bold'] = monochrome_logs ? '' : "\033[1;93m" - colorcodes['blue'] = monochrome_logs ? '' : "\033[0;34m" - colorcodes['purple'] = monochrome_logs ? '' : "\033[0;35m" - colorcodes['cyan'] = monochrome_logs ? '' : "\033[0;36m" - colorcodes['white'] = monochrome_logs ? '' : "\033[0;37m" - colorcodes['red'] = monochrome_logs ? '' : "\033[1;91m" - return colorcodes - } - - static String dashed_line(monochrome_logs) { - Map colors = log_colours(monochrome_logs) - return "-${colors.dim}----------------------------------------------------${colors.reset}-" - } - - static String nf_core(workflow, monochrome_logs) { - Map colors = log_colours(monochrome_logs) - String.format( - """\n - ${dashed_line(monochrome_logs)} - ${colors.green},--.${colors.black}/${colors.green},-.${colors.reset} - ${colors.blue} ___ __ __ __ ___ ${colors.green}/,-._.--~\'${colors.reset} - ${colors.blue} |\\ | |__ __ / ` / \\ |__) |__ ${colors.yellow}} {${colors.reset} - ${colors.blue} | \\| | \\__, \\__/ | \\ |___ ${colors.green}\\`-._,-`-,${colors.reset} - ${colors.green}`._,._,\'${colors.reset} - ${colors.purple} ${workflow.manifest.name} v${workflow.manifest.version}${colors.reset} - ${dashed_line(monochrome_logs)} - """.stripIndent() - ) - } -} diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 44551e0a..2fc0a9b9 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -19,27 +19,16 @@ class NfcoreTemplate { } // - // Check params.hostnames + // Warn if a -profile or Nextflow config has not been provided to run the pipeline // - public static void hostName(workflow, params, log) { - Map colors = logColours(params.monochrome_logs) - if (params.hostnames) { - try { - def hostname = "hostname".execute().text.trim() - params.hostnames.each { prof, hnames -> - hnames.each { hname -> - if (hostname.contains(hname) && !workflow.profile.contains(prof)) { - log.info "=${colors.yellow}====================================================${colors.reset}=\n" + - "${colors.yellow}WARN: You are running with `-profile $workflow.profile`\n" + - " but your machine hostname is ${colors.white}'$hostname'${colors.reset}.\n" + - " ${colors.yellow_bold}Please use `-profile $prof${colors.reset}`\n" + - "=${colors.yellow}====================================================${colors.reset}=" - } - } - } - } catch (Exception e) { - log.warn "[$workflow.manifest.name] Could not determine 'hostname' - skipping check. Reason: ${e.message}." - } + public static void checkConfigProvided(workflow, log) { + if (workflow.profile == 'standard' && workflow.configFiles.size() <= 1) { + log.warn "[$workflow.manifest.name] You are attempting to run the pipeline without any custom configuration!\n\n" + + "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" + + " (1) Using an existing pipeline profile e.g. `-profile docker` or `-profile singularity`\n" + + " (2) Using an existing nf-core/configs for your Institution e.g. `-profile crick` or `-profile uppmax`\n" + + " (3) Using your own local custom config e.g. `-c /path/to/your/custom.config`\n\n" + + "Please refer to the quick start section and usage docs for the pipeline.\n " } } @@ -168,7 +157,6 @@ class NfcoreTemplate { log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed successfully, but with errored process(es) ${colors.reset}-" } } else { - hostName(workflow, params, log) log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-" } } diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index d1bb5ed3..21e9c4ad 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -60,6 +60,9 @@ class WorkflowMain { // Print parameter summary log to screen log.info paramsSummaryLog(workflow, params, log) + // Check that a -profile or Nextflow config has been provided to run the pipeline + NfcoreTemplate.checkConfigProvided(workflow, log) + // Check that conda channels are set-up correctly if (params.enable_conda) { Utils.checkCondaChannels(log) @@ -68,9 +71,6 @@ class WorkflowMain { // Check AWS batch settings NfcoreTemplate.awsBatch(workflow, params) - // Check the hostnames against configured profiles - NfcoreTemplate.hostName(workflow, params, log) - // Check input has been provided if (!params.input) { log.error "Please provide an input samplesheet to the pipeline e.g. '--input samplesheet.csv'" diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index 0a2b79ed..7d765fe9 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -435,8 +435,12 @@ workflow NANOSEQ{ //////////////////////////////////////////////////// workflow.onComplete { -// Completion.email(workflow, params, params.summary_params, log, multiqc_report) - Completion.summary(workflow, params, log) + if (params.email) { + NfcoreTemplate.email(workflow, params, summary_params, projectDir, log, multiqc_report) + //Completion.email(workflow, params, params.summary_params, log, multiqc_report) + } +// Completion.summary(workflow, params, log) + NfcoreTemplate.summary(workflow, params, log) } //////////////////////////////////////////////////// From 6314cbdc5ff4e4ead37ac30b12157fa53848cc10 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Fri, 17 Dec 2021 15:20:21 +0800 Subject: [PATCH 19/43] fix multiqc: Path value cannot contain a new-line character: id: nf-core-nanoseq-summary --- workflows/nanoseq.nf | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/workflows/nanoseq.nf b/workflows/nanoseq.nf index 7d765fe9..fd42720b 100644 --- a/workflows/nanoseq.nf +++ b/workflows/nanoseq.nf @@ -145,9 +145,6 @@ workflow NANOSEQ{ /* * SUBWORKFLOW: Read in samplesheet, validate and stage input files */ - left = Channel.from(['X', 1], ['Y', 2], ['Z', 3], ['P', 7]) - right= Channel.from(['Z', 6], ['Y', 5], ['X', 4]) - left.join(right).view() INPUT_CHECK ( ch_input ) .set { ch_sample } @@ -425,7 +422,7 @@ workflow NANOSEQ{ ch_featurecounts_gene_multiqc.ifEmpty([]), ch_featurecounts_transcript_multiqc.ifEmpty([]), CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect(), - ch_workflow_summary + ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml') ) } } From 670bab4cc55934dd798062d448cc723f1907fde0 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Fri, 17 Dec 2021 15:42:14 +0800 Subject: [PATCH 20/43] update nf-core/nanoseq logos --- assets/nf-core-nanoseq_logo_light.png | Bin 428 -> 11262 bytes docs/images/nf-core-nanoseq_logo_dark.png | Bin 288 -> 75618 bytes docs/images/nf-core-nanoseq_logo_light.png | Bin 519 -> 75499 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/nf-core-nanoseq_logo_light.png b/assets/nf-core-nanoseq_logo_light.png index d46347c2d217dd9a7c090367c55a068226f937c1..313219c02d7587b9c038c117657fd994c571051d 100644 GIT binary patch literal 11262 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 literal 428 zcmZ`#K~BRk5WM>pOV3aj5H~9lG(l7

J-waT~81OO72jb}RoLJM9sUyE8jGGwP=P zT({r52Hx8DE$sF$+szslcT#Se_EpNdtuM&KawTQ+v8aUV!(c1b6Y4w!6D(Cvt4H`E zKADbS@9>1i;=z)V7g#7c)78Q2R21&3KiPRN)L6AW!IagA!AuZn7(F8!n;^O(3&GEL z`IdZS!SoYp3ZUbR4QN~-hc4sVpItBmfoJG&#Lk2yM)p}|n3k}!gi}7^1bED6U@;Ic zl^ka$&sv*cyhCf#Eg<}pQxJ33KjQ@CIv?|QCJpD_6tkR+I~LC|e?kXMA=kc(1G!N4 VJvv>8IrcTl)qtG)%H@^D#V=*Sk$eCE diff --git a/docs/images/nf-core-nanoseq_logo_dark.png b/docs/images/nf-core-nanoseq_logo_dark.png index dee3db82069b61e9a5380d940355f47ff565057c..408f9afcabdb7d677992732f35979138abb9d40a 100644 GIT binary patch literal 75618 zcmeFY`9GBX_Xj?fQ1&D(q!?7PB>OTdq9LU0WQmew%f64hh3-UVLfNJgvL$5SCJK`> zA!HwnkbU0<-!ty}?*0A#2j3q)^LSL~x?b0Lo%1};^E~Hud30G{n|;rrJqQGX9eq*r z3If4GMIac)SeW1^XB+Oyz(2d)FPeKI5FDRqf9PVxISwNbM-gbvb4GqilYODj-uPY< zTVL@qjV>IPW~-A{;W*8Eoy9)#^v%=tci3VQpIj8~EBHG?QY-B&UuoCaa>2VA4#&=k zyX({Sy{A}Pzsxtu1$gA{KO8e5q$PP?p3l=>zW({)wqpk$a@9+7)JYq+2XDVUlz!#~ zIz1q3qsO3q!n@fsEA+?JrD=z(?DCYAg*8_TYika$bmw!K-9C-szi$wZ`h(2>eRERt z|GuXA;{W{c4$wpbF<-|zGC*d(>qqMqqtZ~Dl{NbA9W?|)hPB9nLxEva|c9P2i9&AODy z*ZY*gDa7UlY_w>Zr%#BT6ZXS&Ad%9Iv}_U$&|$v&8nGDDwe}#Kb(Bt?<;$&*7_n(jgL!Tmaz<}H4lZP7b)?&Kb(QX)HEoZ7yQDXL@osSW1w8AQ^n=fRur|+= zZB|#(?jc5wLxI})NE_Wu7hOTFr6Kc(_0|OwcY;W;_i){C#Fw|Rf6Bo<=`(U=azq2M zwJtWdmeJA`5r6%0uCoV4|6As=6L{7h9fBn0A-gOUn*$EvlWojS3ecN{{_h%dxJK&I zyfa!-JUI=MkKCg!{nx&be+_w?)Nh0jeL;yolh9D@X3#1rK!4nRU+DP1ySZ!!HwR)X z#faW%h5Ht1+ig9FIsd=gBf-_+6Di_!tNEUFFDz#)2i>#O*?)@|;i1AAxfe(`^!(!o zsn2|*iJXbk45@nml`;&w%gx(&PZ7VN`^}Vf?XkDo(J9CwAb{{3b(Xmk*X1+Z#{mM}`&+4cnuAmWq?bQ6WB9 zaa^SzMrnPiu5f`h)M|DsjNPGae*W$_+rOTeWu&1LOSQ(vvyF*v9|Z)VlvQfJTVF`Y zM}|J5bkmI}U5yr9dj&p-ez&kmfB1PV=fC$J{sQ1AMzW&cp_&qvpHZ%%U0)>I_}L=L z0EA+WfhxhP!(L(=s^w8DyNxg+RsY`hQ#t_W)gNCdz5xcOBWECZimFd-uL1n*zv-j+v;Lv9zG$1KQZ$`?UU5=04q10c^jtS z9`W873OnqzByTVn!JF~FkN~LG=8@Yzu@!>oc^=vo9MY|sy$O_Dv=8U1kD%`SFXW7T zJF}m1VqAQ@uH(PP+J2$|TX$X^d&lzgG+;LaZe8J!Z_k9E>x->b(e+uW5JPo#a-xCR zE0OT@Pg1;m3osEFj|-MG`#e2kFux8P627Om^aIQdMP=i)yn4fN)US{NY77yKkzBd~ z|KacJPSy>4{rv@{AiCBL_kDb*d}dpcs37Kl1HU1lSMtUS_nPy)#sBb5yJZ4P?LO(V zIRIb@asBSU-XbnuB}ufz-3d*|7QA>@7wg;HETvl9(S;dI|IagLxd8wbII1^RAb5Jc zAE|C}tAK?23Y$jH>!RQ36iA7g^pvR+GjKuD#F?+nQvBjLt^@LaXlLJPaBMYFy>--8 z5G|ubgU-E+jx>aOVi$0ewBQ;kb4n-uNggv_T4w6c4{Y>KwV|zLRchS**oEkf9{$1A zM3%y?Q7N3&&j#0UY)~8cr_LF-=cRJJ&VRztmO{;5G+jnx58e>ONd94~j&Ixsc4}0OUQOL*W;+6a!&Tq|9z2#al zh?7nC|0CRvI(36kh#QXDeCP(~EKc>9FBsuD#b9%BCZ&iUre+w)Xp{7QaP407gLsv<(; z+@D<*EoPze29h8om9)F=|L?BrH*d?Jx6JVsRgUbh5p4gf6&+th(9266n+)hJzb6F< zHunG7{)KbJe{*HWZ1i5tvDbgRMS#?Z67e^N)!ZcDp2PpUr#2SdJT<%y70j8o+W+f0 zwVX@-n?2zB4kQcVz2Tq2@-m>wzp8-Lx8+4!O8C!GOjECs73;mD!_M4{wg>)rM*5a1 zSfzoUws?FLQFrmr*3WG;qw==rb|?i+FMa&K@Mb(j9N-&VFt zmAETSW6|yeu?Na!N=Iz=&(XoK48(!Eaf|djiyqKZh~xO4F(8S zIfdS~@jvfGKG(1CHpgb`jE=SGLCfVx*_HjX;l3aS_2-I|!#d1kZNTK>V2<#{dgaz2 z_^i$u%V-<%=c1?j6-6qzaikJq@*m=CKTsX8-@xp_d}K)+ z2VGK(8oGF?Ec*X*jiy(3E|kK{%G&t;)7FWK7Rrq7TI)>?*Jc72F#e}OA|Hz5 ztW1bN|?jo@KdBojhpNU>% z-f?(F`o`G@K$4#>uU3KaSjG>TQf`N$Fny#^u`E3d0Oqr5^!OX-txXE(kh*qHh4DjO+aD360sV|C#Ix8^lXz@$?0WUQ}ut_ ze@FEhgu@war#&|WU#4i@R zjbEE?T1RoZ8kAUh&7G+B{m_4>H&=^MqmrM2WkTJJq2AORI0PdI3OE**FC?e<(Sz;6Jj%4DkZ!Wqk zqtq&|_^lmxwEg~I*@LsPp*{OZp57^dW={9C@pMV>%M%DK8o|aIy2)cH1zx(SyyoAe z{j`l^>{f=yQ#4I@qs9E)#<>XQ=2L;ofy@c?wweIb_k&AulL0PR{KdN-gNn54-@o$i z&doP+Q_8b}@GqCpwqT4OX08zw(>Ob3EF3W)Q$`_o-06(#zMlR zAxmhvQ1mUkcP1$;9dUpgB7aA5>88DDxd>jNzI#WPea}0r%RF|~vr>NUGR-8*sr!Nt z1TnPqvaNdytK+CvSp21eB6oh8NpF*YNzhp5|Cofqu9lOae2B(xzrShy{#Ngd1x106 zD%jDAIUVe242$D!Xq2@wqcm5x1a&g$MS_>nxi3b&6;3;Y^1`Nn0W zGfLDy=<~8~Rg;ET@TM(Rn=fOudAR|hC=C1F2hk`-u023*pG=9SJgNg%E_k&+h>zam=Z<`FP$ z_R)&Kbr-{n@iujnw|-73X$$7w>12}9^ec>Y=O4BztuGeOb>C+oOEZG*!xc(VDb#`L z5a&^6(dH%`t6D?J6YJZcLks$gTLIyIN5%G>ZD=~{3)mE}9*7{kr-h(zv3n&?_pWa9 z^n;y#Z&q^vKaEv1_BcgU(FsQ|{mO|_QLmhs4!!*^tb+iwCcGnP{vFxQ9B;8K}M6CnqW-y=7YQEzcmOeA( zvv^kls3*Y5($bD{{*KPs#b=1i(5FVK-<{1Go!+hpk5Y46*&=nWSe(UR!A8`^hw)|tNQWhKtw>83=%pvA7H~JP~rF>uj|tL??j4pB|DJNJ)Oxz z&eK*-fNl=U|Cr&CQ;na?Pn zl(O%{prhJwt?_{NQePs40o`A$sOJ09>%dD@nkARO5=kO+o6Fjn1OF7hAmfa_?nH;v zeK+i~hVW3=wk*fFE{j0&>S7;bt)B9Tq*VD|f8=P2ZHv3v@o9d(wVAZpcg^z1TJ5Wu z%D|rLk+hM%?6McltMkL5TT9VXe%<1(J@b!9*2~nUxQ2%2+q?7sb{2znTkmn3Ctgy3 z={SgDj9g3Ne7 z^2F)}2-;kh>3k6|7`9wB+PjdZUGpnOYR24EDtS&erDniqvU%ILlaF+(>z$sxJtu$V zqW{|bnUdt1^*0)TNClC#&ClaXHKul-fCi=A0j8hF-<`;wJmdF|GmvOt`Zy#W?VF`- zO#KP~Q3p|R_yW*JGUYc?O6RU|`PHC@*KcnvY=?{!Sf`clZgWbg9N)+>%dYqO3ef+359xQ++Gal!gOi${hxYNmGSsEhII%zL8y^A*)PAqZxc zuwyoWgP$E%U9X_XEAH4o4cl4eDHAO9M!D{$(dSSoIF+xwSj-Fn4yXFAlQ>1o2y z+_pUmTUdrF{VAc$S$9WwVjvxX$I&$H@DaRS4e6M>q=Pa|vz%YsLRK`1c9+)Iv?`ZQ zd}Q$LM6Il*j5N1C$gWCG>myZ85fqX$78e|5bmo77OB-~U>6l=6fhjbh)x#2OWtRIh z!Y+!D_Dq*GK>Hyl&cD!yj*f=2}3E@3EFTO2z0c;!yTc472rqp2k6 zWct>Jnvl*0!76fD+)6;fJ#>gXF%LA4L@WmbTeI0X0>SoRB6V`74oI<(o2dZbjkb)Z&P}P+a2TNSKKa978EawIP z$`{A4{TyD&F|OXz^McZerTRmfe(*YA>kcAAQ5>9a1DdpXr*{{ofUaH~??(!r5TIwu z1RVniQpY=-CR$;G9|*HQX^#6=f!g>sYO~q+xZ2-w!+(1gmw0>F(PHRe&$7CDp-5BH zlpMR+wRo{L;3Ypjh>opUD=WqrSkn?4bdo5NR`}nfAo4xva0#L?x;VbMHM}#rQ?%b) znUU>0n0J&&dZV%yqT3~#I((3svu`7ELp@O1MtQv!nTY>gu1o3)=?Qy`_gzxHke#*t zobnB_|BK;eJ1EEQ@*yX_pmz8;<()y%7Zj+J&~YyJV!($Ic&-FT)x+W=3rOYb8_7K@ zQ#I6Grl^3=)eQ?pmZ&+SPJs>TEAe7}<#{q~ZM=TLWjA@BXB_l7DWHb3ueixKqvxe` zMrjYg7`8Z-)MI;hw7(K)|0~LjIVOhTGuuNgT75~#@CR)XvC+4 z4x~SttI+o>`bMgra1*rYv~BmgR<0X#%}kl)y)vXtPKV_X+fHk4W*~*Wdk$*5na*7( z@$W6b`f?2O5In*Z(@}cgwe~#x5-0JU9fRJxfW~55G<6XCFUj^zqWIt!c zQKJJ2=di8kFk7Gpzn26QdKi|c-lGK@v*c+Hl5w2?sWI4k?4=DX-wPL4ptp|qoo77! z!+unCbe?3rq^B|ut_hi6Dc<}hoK8)8F*PnZvs6!8lcx6$)$Ao5+wr$6B*F#sI|=A7 z6yiA$o1nWg6%KDZmpA!Vz4kiN{Pl`U(8N}gjk~d1H|b+u$Z%7GPC-}pS{U(z} ztwP9u2kUmJdT7t++E+|(eBHdDVN7y^bcv_+TL+;&HJep+UBq^^D$$I!eFp51jJCc? zQ)!3t^gyLKzJcdIrCdc@n=7+AW)$97c(=K|`kGI2rq{<3duM$p-ljCO(CdTOyFCYW z=R=qEx0vJ=Vcm5PVNX4F7^Q}B#9;3)3lOSCm@ ztMtT_GQ1*C{JQhdnUOQWHM!_tIO_HBQ#rXBq>x`1n5)Elu`J4a-pEr+)Pk6QOxIA^JP0e(KXwuE|`VWtG3{__00i}S3YpbVfy5ZZum;KC1H zJ0kSJY2(D{g?uR1*U~fynTuagf{_KBIrXH@#3xO(IRXqwz|F`18B1uPL2TeC#%Z35 zFXgU(YfFc)#nsWh!zf9i}W{u zd+uC&PN8XOF+lgM7mN_AyjW=4Kfs&pN|>*{JJ4K9pqj}$r**+-Oq z9tBeMQy#eb&pArub5oJ7Ys#8}9Af^aKtctOU#-ZRE6U7lFIFG`0`M5fdDW_ke`bR_ zjDn@q(^U66*!C+ZS!vMZN)g?Fp*3!~?Hjk%pzY)XtzaYh;{T)VEM$BfM@xpK4l^cp zrw}gCSe0JmcM+t~U$DxLc6frV#+km1P;D%~RlY=;N{GKN_L^bwF4!75)7Yh)BuHyz zr3C2p?_R~SLKQn^qC4M&4tejzb>Ifg%Geo60o!|lM~N=zty9DXoazTqe~VtG?-UN( zkH>}hh?N~B8ezwg9p$t&kJ6?MUIS8D*u;OW@~;-`x7YYl=`30m zO0!n_d>=0ssMx1)+7lyv|7Ql6B{}t;RLuKa3rzN6mVgQ@BFytb8N$M69dPT5P-XROe_Xl_1 zD{q0SZjVMKQR=`$^~f451<(0=nU*PUA3|M{BRcECnCaWje8@I!`za*}ym;Tr656Cn zMYRm-?p;QiA{>PE@+>b5`_L^cOyDKJfzO&T((?DIwvR9ByRrZzg9v{3|E@A@;4Rz<2Ta|1ztE7p#5In;-kWS-PSCr4% zZOcJ>pe8aMOd4CZl3`>eb$zxq+ENr%0D6Aha9ZZvy3Owj0R@{kHIA1_W(q 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 QwgSyWD-BsM+%TVX=zX#so# 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�ev`Q}H7y#R1zKkC5n?0_OP7^FqWWeXhX0t0pNK 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

)bNtXB<>| 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 Date: Fri, 17 Dec 2021 19:03:00 +0800 Subject: [PATCH 21/43] remove warnings from conf/modules.config --- conf/modules.config | 84 ++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 3f0e1632..cca63e60 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -128,21 +128,31 @@ if (!params.skip_alignment) { } if (params.aligner == "graphmap2") { process { - withName: SAMTOOLS_SORT { - publishDir = [ - path: { "${params.outdir}/graphmap2" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - withName: SAMTOOLS_INDEX { - publishDir = [ - path: { "${params.outdir}/graphmap2" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.call_variants){ + withName: SAMTOOLS_SORT { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_INDEX { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } else { + withName: SAMTOOLS_SORT_INDEX { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - withName: BAM_STATS_SAMTOOLS { + withName: NFCORE_NANOSEQ:NANOSEQ:BAM_SORT_INDEX_SAMTOOLS:BAM_STATS_SAMTOOLS { publishDir = [ path: { "${params.outdir}/graphmap2/samtools_stats" }, mode: 'copy', @@ -152,7 +162,7 @@ if (!params.skip_alignment) { } if (!params.skip_bigbed) { process { - withName: BEDTOOLS_UCSC_BIGBED { + withName: NFCORE_NANOSEQ:NANOSEQ:BEDTOOLS_UCSC_BIGBED { publishDir = [ path: { "${params.outdir}/graphmap2/bigbed" }, mode: 'copy', @@ -163,7 +173,7 @@ if (!params.skip_alignment) { } if (!params.skip_bigwig) { process { - withName: BEDTOOLS_UCSC_BIGWIG { + withName: NFCORE_NANOSEQ:NANOSEQ:BEDTOOLS_UCSC_BIGWIG { publishDir = [ path: { "${params.outdir}/graphmap2/bigwig" }, mode: 'copy', @@ -175,21 +185,31 @@ if (!params.skip_alignment) { } if (params.aligner == "minimap2") { process { - withName: SAMTOOLS_SORT { - publishDir = [ - path: { "${params.outdir}/minimap2" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - withName: SAMTOOLS_INDEX { - publishDir = [ - path: { "${params.outdir}/minimap2" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.call_variants){ + withName: SAMTOOLS_SORT { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_INDEX { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } else { + withName: SAMTOOLS_SORT_INDEX { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } - withName: BAM_STATS_SAMTOOLS { + withName: NFCORE_NANOSEQ:NANOSEQ:BAM_SORT_INDEX_SAMTOOLS:BAM_STATS_SAMTOOLS { publishDir = [ path: { "${params.outdir}/minimap2/samtools_stats" }, mode: 'copy', @@ -199,7 +219,7 @@ if (!params.skip_alignment) { } if (!params.skip_bigbed) { process { - withName: BEDTOOLS_UCSC_BIGBED { + withName: NFCORE_NANOSEQ:NANOSEQ:BEDTOOLS_UCSC_BIGBED { publishDir = [ path: { "${params.outdir}/minimap2/bigbed" }, mode: 'copy', @@ -210,7 +230,7 @@ if (!params.skip_alignment) { } if (!params.skip_bigwig) { process { - withName: BEDTOOLS_UCSC_BIGWIG { + withName: NFCORE_NANOSEQ:NANOSEQ:BEDTOOLS_UCSC_BIGWIG { publishDir = [ path: { "${params.outdir}/minimap2/bigwig" }, mode: 'copy', From 6102586c54bbbc089e0a14a2ec4ba897fae4096a Mon Sep 17 00:00:00 2001 From: Christopher Hakkaart Date: Fri, 17 Dec 2021 17:33:45 +0100 Subject: [PATCH 22/43] New configuration to tidy results folder --- conf/modules.config | 208 ++++++++++++++++++++++++++++++++------------ 1 file changed, 153 insertions(+), 55 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index cca63e60..a490834e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -14,7 +14,8 @@ process { publishDir = [ path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: 'copy', + mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] @@ -23,15 +24,17 @@ process { publishDir = [ path: { "${params.outdir}/pipeline_info" }, mode: 'copy', + enabled: true, pattern: '*_versions.yml' ] } // INPUT_CHECK - withName: 'NFCORE_NANOSEQ:NANOSEQ:INPUT_CHECK:SAMPLESHEET_CHECK' { + withName: SAMPLESHEET_CHECK { publishDir = [ path: { "${params.outdir}/pipeline_info" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -43,6 +46,7 @@ if (!params.skip_basecalling) { publishDir = [ path: { "${params.outdir}/guppy" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -55,6 +59,7 @@ if (params.skip_basecalling && !params.skip_demultiplexing) { publishDir = [ path: { "${params.outdir}/qcat" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -67,6 +72,7 @@ if (params.run_nanolyse) { publishDir = [ path: { "${params.outdir}/nanolyse" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -80,6 +86,7 @@ if (!params.skip_qc) { publishDir = [ path: { "${params.outdir}/pycoqc" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -91,6 +98,7 @@ if (!params.skip_qc) { publishDir = [ path: { "${params.outdir}/nanoplot" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -102,6 +110,7 @@ if (!params.skip_qc) { publishDir = [ path: { "${params.outdir}/fastqc" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -115,6 +124,7 @@ if (!params.skip_alignment) { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -122,50 +132,70 @@ if (!params.skip_alignment) { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } if (params.aligner == "graphmap2") { process { - if (!params.call_variants){ - withName: SAMTOOLS_SORT { - publishDir = [ - path: { "${params.outdir}/graphmap2" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - withName: SAMTOOLS_INDEX { - publishDir = [ - path: { "${params.outdir}/graphmap2" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } else { - withName: SAMTOOLS_SORT_INDEX { - publishDir = [ - path: { "${params.outdir}/graphmap2" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + withName: SAMTOOLS_SORT { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + enabled: false, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } - withName: NFCORE_NANOSEQ:NANOSEQ:BAM_SORT_INDEX_SAMTOOLS:BAM_STATS_SAMTOOLS { + withName: SAMTOOLS_VIEW_BAM { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + enabled: false, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_INDEX { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + enabled: true, + pattern: "*{'.sorted.bam','.bai'}", + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_STATS { + publishDir = [ + path: { "${params.outdir}/graphmap2/samtools_stats" }, + mode: 'copy', + enabled: true, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_IDXSTATS { + publishDir = [ + path: { "${params.outdir}/graphmap2/samtools_stats" }, + mode: 'copy', + enabled: true, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_FLAGSTAT { publishDir = [ path: { "${params.outdir}/graphmap2/samtools_stats" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } if (!params.skip_bigbed) { process { - withName: NFCORE_NANOSEQ:NANOSEQ:BEDTOOLS_UCSC_BIGBED { + withName: UCSC_BED12TOBIGBED { publishDir = [ path: { "${params.outdir}/graphmap2/bigbed" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -173,56 +203,102 @@ if (!params.skip_alignment) { } if (!params.skip_bigwig) { process { - withName: NFCORE_NANOSEQ:NANOSEQ:BEDTOOLS_UCSC_BIGWIG { + withName: BEDTOOLS_GENOMECOV { publishDir = [ path: { "${params.outdir}/graphmap2/bigwig" }, mode: 'copy', + enabled: true, + pattern: "*bedGraph", saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } - } - } - if (params.aligner == "minimap2") { - process { - if (!params.call_variants){ - withName: SAMTOOLS_SORT { - publishDir = [ - path: { "${params.outdir}/minimap2" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - withName: SAMTOOLS_INDEX { - publishDir = [ - path: { "${params.outdir}/minimap2" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } else { - withName: SAMTOOLS_SORT_INDEX { + process { + withName: UCSC_BEDGRAPHTOBIGWIG { publishDir = [ - path: { "${params.outdir}/minimap2" }, + path: { "${params.outdir}/graphmap2/bigwig" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } - withName: NFCORE_NANOSEQ:NANOSEQ:BAM_SORT_INDEX_SAMTOOLS:BAM_STATS_SAMTOOLS { + } + } + if (params.aligner == "minimap2") { + process { + withName: MINIMAP2_ALIGN { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + enabled: false, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: MINIMAP2_INDEX { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + enabled: false, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_SORT { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + enabled: false, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_VIEW_BAM { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + enabled: false, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_INDEX { + publishDir = [ + path: { "${params.outdir}/minimap2" }, + mode: 'copy', + enabled: true, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_STATS { + publishDir = [ + path: { "${params.outdir}/minimap2/samtools_stats" }, + mode: 'copy', + enabled: true, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_IDXSTATS { + publishDir = [ + path: { "${params.outdir}/minimap2/samtools_stats" }, + mode: 'copy', + enabled: true, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_FLAGSTAT { publishDir = [ path: { "${params.outdir}/minimap2/samtools_stats" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } if (!params.skip_bigbed) { process { - withName: NFCORE_NANOSEQ:NANOSEQ:BEDTOOLS_UCSC_BIGBED { + withName: UCSC_BED12TOBIGBED { publishDir = [ path: { "${params.outdir}/minimap2/bigbed" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -230,10 +306,22 @@ if (!params.skip_alignment) { } if (!params.skip_bigwig) { process { - withName: NFCORE_NANOSEQ:NANOSEQ:BEDTOOLS_UCSC_BIGWIG { + withName: BEDTOOLS_GENOMECOV { + publishDir = [ + path: { "${params.outdir}/minimap2/bigwig" }, + mode: 'copy', + enabled: true, + pattern: "*bedGraph", + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + process { + withName: UCSC_BEDGRAPHTOBIGWIG { publishDir = [ path: { "${params.outdir}/minimap2/bigwig" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -247,8 +335,9 @@ if (params.call_variants) { process { withName: MEDAKA_VARIANT { publishDir = [ - path: { "${params.outdir}/DNA_variant_calling/medaka_variant" }, + path: { "${params.outdir}/dna_variant_calling/medaka_variant" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -258,8 +347,9 @@ if (params.call_variants) { process { withName: SNIFFLES { publishDir = [ - path: { "${params.outdir}/DNA_variant_calling/sniffles" }, + path: { "${params.outdir}/dna_variant_calling/sniffles" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -274,6 +364,7 @@ if (!params.skip_quantification) { publishDir = [ path: { "${params.outdir}/bambu" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -284,6 +375,7 @@ if (!params.skip_quantification) { publishDir = [ path: { "${params.outdir}/bambu/deseq2" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -291,6 +383,7 @@ if (!params.skip_quantification) { publishDir = [ path: { "${params.outdir}/bambu/dexseq" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -303,6 +396,7 @@ if (!params.skip_quantification) { publishDir = [ path: { "${params.outdir}/stringtie2" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -310,6 +404,7 @@ if (!params.skip_quantification) { publishDir = [ path: { "${params.outdir}/stringtie2/featureCounts" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -320,6 +415,7 @@ if (!params.skip_quantification) { publishDir = [ path: { "${params.outdir}/bambu/deseq2" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -327,6 +423,7 @@ if (!params.skip_quantification) { publishDir = [ path: { "${params.outdir}/bambu/dexseq" }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -345,6 +442,7 @@ if (!params.skip_multiqc) { params.skip_alignment? '' : "/${params.aligner}" ].join('') }, mode: 'copy', + enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } From 755584eaf526c368f756e877a9a7b51cac7848ed Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Sat, 18 Dec 2021 22:19:51 +0800 Subject: [PATCH 23/43] update conf/modules.config --- conf/modules.config | 88 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 7 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index a490834e..e422a8b2 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -28,7 +28,14 @@ process { pattern: '*_versions.yml' ] } - + withName: GET_TEST_DATA { + publishDir = [ + path: { "${params.outdir}/test-dataset" }, + mode: 'copy', + enabled: false, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } // INPUT_CHECK withName: SAMPLESHEET_CHECK { publishDir = [ @@ -67,6 +74,14 @@ if (params.skip_basecalling && !params.skip_demultiplexing) { } if (params.run_nanolyse) { + withName: GET_NANOLYSE_FASTA { + publishDir = [ + path: { "${params.outdir}/nanolyse" }, + mode: 'copy', + enabled: false, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } process { withName: NANOLYSE { publishDir = [ @@ -139,7 +154,15 @@ if (!params.skip_alignment) { } if (params.aligner == "graphmap2") { process { - withName: SAMTOOLS_SORT { + withName: GRAPHMAP2_INDEX { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + enabled: false, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: GRAPHMAP2_ALIGN { publishDir = [ path: { "${params.outdir}/graphmap2" }, mode: 'copy', @@ -155,7 +178,25 @@ if (!params.skip_alignment) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } + withName: SAMTOOLS_SORT { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + enabled: true, + pattern: "*.sorted.bam", + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } withName: SAMTOOLS_INDEX { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + enabled: true, + pattern: "*.bai", + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_SORT_INDEX { publishDir = [ path: { "${params.outdir}/graphmap2" }, mode: 'copy', @@ -191,11 +232,21 @@ if (!params.skip_alignment) { } if (!params.skip_bigbed) { process { + withName: BEDTOOLS_BAMBED { + publishDir = [ + path: { "${params.outdir}/graphmap2/bigbed" }, + mode: 'copy', + enabled: true, + pattern: "*.bed12", + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } withName: UCSC_BED12TOBIGBED { publishDir = [ path: { "${params.outdir}/graphmap2/bigbed" }, mode: 'copy', enabled: true, + pattern: "*.bigBed", saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -219,6 +270,7 @@ if (!params.skip_alignment) { path: { "${params.outdir}/graphmap2/bigwig" }, mode: 'copy', enabled: true, + pattern: "*bigWig", saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -227,7 +279,7 @@ if (!params.skip_alignment) { } if (params.aligner == "minimap2") { process { - withName: MINIMAP2_ALIGN { + withName: MINIMAP2_INDEX { publishDir = [ path: { "${params.outdir}/minimap2" }, mode: 'copy', @@ -235,7 +287,7 @@ if (!params.skip_alignment) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - withName: MINIMAP2_INDEX { + withName: MINIMAP2_ALIGN { publishDir = [ path: { "${params.outdir}/minimap2" }, mode: 'copy', @@ -243,7 +295,7 @@ if (!params.skip_alignment) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - withName: SAMTOOLS_SORT { + withName: SAMTOOLS_VIEW_BAM { publishDir = [ path: { "${params.outdir}/minimap2" }, mode: 'copy', @@ -251,11 +303,12 @@ if (!params.skip_alignment) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - withName: SAMTOOLS_VIEW_BAM { + withName: SAMTOOLS_SORT { publishDir = [ path: { "${params.outdir}/minimap2" }, mode: 'copy', - enabled: false, + enabled: true, + pattern: "*.sorted.bam", saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -264,6 +317,16 @@ if (!params.skip_alignment) { path: { "${params.outdir}/minimap2" }, mode: 'copy', enabled: true, + pattern: "*.bai", + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: SAMTOOLS_SORT_INDEX { + publishDir = [ + path: { "${params.outdir}/graphmap2" }, + mode: 'copy', + enabled: true, + pattern: "*{'.sorted.bam','.bai'}", saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -294,11 +357,21 @@ if (!params.skip_alignment) { } if (!params.skip_bigbed) { process { + withName: BEDTOOLS_BAMBED { + publishDir = [ + path: { "${params.outdir}/minimap2/bigbed" }, + mode: 'copy', + enabled: true, + pattern: "*.bed12", + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } withName: UCSC_BED12TOBIGBED { publishDir = [ path: { "${params.outdir}/minimap2/bigbed" }, mode: 'copy', enabled: true, + pattern: "*.bigBed", saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -322,6 +395,7 @@ if (!params.skip_alignment) { path: { "${params.outdir}/minimap2/bigwig" }, mode: 'copy', enabled: true, + pattern: "*.bigWig", saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } From 4bffca2c06a20761ae82d02280401e53886441b9 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan <41866052+yuukiiwa@users.noreply.github.com> Date: Sat, 18 Dec 2021 22:23:48 +0800 Subject: [PATCH 24/43] fix linting error --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index e422a8b2..b7cf744d 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -14,7 +14,7 @@ process { publishDir = [ path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: 'copy', + mode: 'copy', enabled: true, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] From 5e2e897256a4982c8164be9f09ff767f44052414 Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Mon, 20 Dec 2021 15:41:28 +0100 Subject: [PATCH 25/43] Update output.md Update variant calling output documentation --- docs/output.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/output.md b/docs/output.md index 67030f6a..b25d8657 100644 --- a/docs/output.md +++ b/docs/output.md @@ -202,6 +202,22 @@ After genomic alignment, novel transcripts can be reconstructed using tools such *Description*: If multiple conditions and multiple replicates are available then the pipeline is able to run differential analysis on gene and transcripts with DESeq2 and DEXSeq, respectively. These steps won't be run if you provide the `--skip_quantification` or `--skip_differential_analysis` parameters or if all of the samples in the samplesheet don't have the same fasta and GTF reference files. +## Variant calling + +

+Output files + +* `minimap2/medaka//round_1.vcf ` + VCF file with small variants for each sample. +* `minimap2/sniffles/_sniffles.vcf` + VCF files with unflitered structural variants. + +*Documentation*: +[Medaka](https://github.com/nanoporetech/medaka), [Sniffles](https://github.com/fritzsedlazeck/Sniffles) + +*Description*: +If the protocol is set to `--protocol DNA` and the *Minimap2* aligner was used, then the `--call_variants` parameter can be envoked to call small variants and structural variants using Medaka and Sniffles, respectively. These steps won't be run if you provide the `--skip_medaka` or `--skip_sniffles` parameters. + ## MultiQC
From 18bb9537bde81ad719769fecf8fbff80e87e2d08 Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Mon, 20 Dec 2021 15:42:37 +0100 Subject: [PATCH 26/43] Update output.md Fix format error --- docs/output.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/output.md b/docs/output.md index b25d8657..cff5ab21 100644 --- a/docs/output.md +++ b/docs/output.md @@ -212,6 +212,8 @@ If multiple conditions and multiple replicates are available then the pipeline i * `minimap2/sniffles/_sniffles.vcf` VCF files with unflitered structural variants. +
+ *Documentation*: [Medaka](https://github.com/nanoporetech/medaka), [Sniffles](https://github.com/fritzsedlazeck/Sniffles) From ce5ead26e14375d26ba6d25f89aa6757686803bc Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Mon, 20 Dec 2021 15:43:39 +0100 Subject: [PATCH 27/43] Update output.md Correct spelling mistake --- docs/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index cff5ab21..9b498c7e 100644 --- a/docs/output.md +++ b/docs/output.md @@ -218,7 +218,7 @@ If multiple conditions and multiple replicates are available then the pipeline i [Medaka](https://github.com/nanoporetech/medaka), [Sniffles](https://github.com/fritzsedlazeck/Sniffles) *Description*: -If the protocol is set to `--protocol DNA` and the *Minimap2* aligner was used, then the `--call_variants` parameter can be envoked to call small variants and structural variants using Medaka and Sniffles, respectively. These steps won't be run if you provide the `--skip_medaka` or `--skip_sniffles` parameters. +If the protocol is set to `--protocol DNA` and the *Minimap2* aligner was used, then the `--call_variants` parameter can be invoked to call small variants and structural variants using Medaka and Sniffles, respectively. These steps won't be run if you provide the `--skip_medaka` or `--skip_sniffles` parameters. ## MultiQC From 7e5c5b0ea560dc9de09684fa70f43d1a8ec5024a Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Mon, 20 Dec 2021 15:45:46 +0100 Subject: [PATCH 28/43] Update output.md Fix linting error --- docs/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index 9b498c7e..2cf5c222 100644 --- a/docs/output.md +++ b/docs/output.md @@ -207,7 +207,7 @@ If multiple conditions and multiple replicates are available then the pipeline i
Output files -* `minimap2/medaka//round_1.vcf ` +* `minimap2/medaka//round_1.vcf` VCF file with small variants for each sample. * `minimap2/sniffles/_sniffles.vcf` VCF files with unflitered structural variants. From 4df1c70b71c30eba79ad5149ee165df2cc1d914d Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan <41866052+yuukiiwa@users.noreply.github.com> Date: Tue, 21 Dec 2021 08:37:16 +0800 Subject: [PATCH 29/43] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0de86232..19d14b8b 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ An example input samplesheet for performing both basecalling and demultiplexing ## Credits -nf-core/nanoseq was originally written by [Chelsea Sawyer](https://github.com/csawye01) and [Harshil Patel](https://github.com/drpatelh) from [The Bioinformatics & Biostatistics Group](https://www.crick.ac.uk/research/science-technology-platforms/bioinformatics-and-biostatistics/) for use at [The Francis Crick Institute](https://www.crick.ac.uk/), London. Other primary contributors include [Laura Wratten](https://github.com/lwratten), [Ying Chen](https://github.com/cying111), [Yuk Kei Wan](https://github.com/yuukiiwa) and [Jonathan Goeke](https://github.com/jonathangoeke) from the [Genome Institute of Singapore](https://www.a-star.edu.sg/gis), [Johannes Alneberg](https://github.com/alneberg) and [Franziska Bonath](https://github.com/FranBonath) from [SciLifeLab](https://www.scilifelab.se/), Sweden. +nf-core/nanoseq was originally written by [Chelsea Sawyer](https://github.com/csawye01) and [Harshil Patel](https://github.com/drpatelh) from [The Bioinformatics & Biostatistics Group](https://www.crick.ac.uk/research/science-technology-platforms/bioinformatics-and-biostatistics/) for use at [The Francis Crick Institute](https://www.crick.ac.uk/), London. Other primary contributors include [Laura Wratten](https://github.com/lwratten), [Ying Chen](https://github.com/cying111), [Yuk Kei Wan](https://github.com/yuukiiwa) and [Jonathan Goeke](https://github.com/jonathangoeke) from the [Genome Institute of Singapore](https://www.a-star.edu.sg/gis), [Christopher Hakkaart](https://github.com/christopher-hakkaart) from [Institute of Medical Genetics and Applied Genomics](https://www.medizin.uni-tuebingen.de/de/das-klinikum/einrichtungen/institute/medizinische-genetik-und-angewandte-genomik), Germany, [Johannes Alneberg](https://github.com/alneberg) and [Franziska Bonath](https://github.com/FranBonath) from [SciLifeLab](https://www.scilifelab.se/), Sweden. Many thanks to others who have helped out along the way too, including (but not limited to): [@crickbabs](https://github.com/crickbabs), [@AnnaSyme](https://github.com/AnnaSyme). From 2c939e84af38f9c80787ea6eaaeb4fe60261e0f5 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan Date: Tue, 21 Dec 2021 13:00:38 +0800 Subject: [PATCH 30/43] update CHANGELOG --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ modules/local/bambu.nf | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35bb59c2..207ee578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,38 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.0.0] - ? + +### Major enhancements + +* Add DNA variant calling functionality +* Port pipeline to the updated Nextflow DSL2 syntax adopted on nf-core/modules + * Removed `--publish_dir_mode` as it is no longer required for the new syntax +* Bump minimum Nextflow version from 21.04.0 -> 21.10.3 +* Update pipeline template to nf-core/tools `2.2` +* Update `bambu` version from `1.0.2` to `2.0.0` + +### Parameters + +* Added `--call_variants` to detect DNA variants +* Added `--split_mnps` to +* Added `--phase_vcf` to +* Added `--skip_medaka` to skip `medaka variant` +* Added `--skip_sniffles` to skip `sniffles` + +### Software dependencies + +| Dependency | Old version | New version | +|-------------------------|-------------|-------------| +| `bioconductor-bambu` | 1.0.2 | 2.0.0 | +| `bioconductor-bsgenome` | 1.58.0 | 1.62.0 | +| `medaka` | | 1.4.4 | +| `sniffles` | | 1.0.12 | + +> **NB:** Dependency has been __updated__ if both old and new version information is present. +> **NB:** Dependency has been __added__ if just the new version information is present. +> **NB:** Dependency has been __removed__ if version information isn't present. + ## [2.0.1] - 2021-11-29 ### Bug fix diff --git a/modules/local/bambu.nf b/modules/local/bambu.nf index 599e351f..6fe8b3e5 100644 --- a/modules/local/bambu.nf +++ b/modules/local/bambu.nf @@ -1,7 +1,7 @@ process BAMBU { label 'process_medium' - conda (params.enable_conda ? "conda-forge::r-base=4.0.3 bioconda::bioconductor-bambu=2.0.0 bioconda::bioconductor-bsgenome=1.58.0" : null) + conda (params.enable_conda ? "conda-forge::r-base=4.0.3 bioconda::bioconductor-bambu=2.0.0 bioconda::bioconductor-bsgenome=1.62.0" : null) container "docker.io/yuukiiwa/nanoseq:bambu_bsgenome" //not on biocontainers; does not have a singularity container input: From 447806f4f71eb7362cd93054d93785dddf4bf323 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan <41866052+yuukiiwa@users.noreply.github.com> Date: Tue, 21 Dec 2021 13:03:08 +0800 Subject: [PATCH 31/43] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 207ee578..bc2325aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Parameters * Added `--call_variants` to detect DNA variants -* Added `--split_mnps` to +* Added `--split_mnps` to * Added `--phase_vcf` to * Added `--skip_medaka` to skip `medaka variant` * Added `--skip_sniffles` to skip `sniffles` From 0d25fa4a9f409c99365a316421c7f0546fbdd687 Mon Sep 17 00:00:00 2001 From: Yuk Kei Wan <41866052+yuukiiwa@users.noreply.github.com> Date: Tue, 21 Dec 2021 13:28:14 +0800 Subject: [PATCH 32/43] add DNA-specific downstream analysis --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 19d14b8b..eb3510f8 100644 --- a/README.md +++ b/README.md @@ -41,12 +41,14 @@ On release, automated continuous integration tests run the pipeline on a [full-s * Each sample can be mapped to its own reference genome if multiplexed in this way * Convert SAM to co-ordinate sorted BAM and obtain mapping metrics ([`SAMtools`](http://www.htslib.org/doc/samtools.html)) 6. Create bigWig ([`BEDTools`](https://github.com/arq5x/bedtools2/), [`bedGraphToBigWig`](http://hgdownload.soe.ucsc.edu/admin/exe/)) and bigBed ([`BEDTools`](https://github.com/arq5x/bedtools2/), [`bedToBigBed`](http://hgdownload.soe.ucsc.edu/admin/exe/)) coverage tracks for visualisation -7. RNA-specific downstream analysis: +7. DNA-specific downstream analysis: + * DNA variant calling ([`medaka`](https://github.com/nanoporetech/medaka) and/or [`sniffles`](https://github.com/fritzsedlazeck/Sniffles)) +8. RNA-specific downstream analysis: * Transcript reconstruction and quantification ([`bambu`](https://bioconductor.org/packages/release/bioc/html/bambu.html) or [`StringTie2`](https://ccb.jhu.edu/software/stringtie/)) * bambu performs both transcript reconstruction and quantification. * When StringTie2 is chosen, each sample can be processed individually and combined. After which, [`featureCounts`](http://bioinf.wehi.edu.au/featureCounts/) will be used for both gene and transcript quantification. - * Differential expression analysis ([`DESeq2`](https://bioconductor.org/packages/release/bioc/html/DESeq2.html) or [`DEXSeq`](https://bioconductor.org/packages/release/bioc/html/DEXSeq.html)) -8. Present QC for raw read and alignment results ([`MultiQC`](https://multiqc.info/docs/)) + * Differential expression analysis ([`DESeq2`](https://bioconductor.org/packages/release/bioc/html/DESeq2.html) and/or [`DEXSeq`](https://bioconductor.org/packages/release/bioc/html/DEXSeq.html)) +9. Present QC for raw read and alignment results ([`MultiQC`](https://multiqc.info/docs/)) ## Quick Start From 3c76d9306fb161f443c4f78a9ff4decfc765fb7e Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Tue, 21 Dec 2021 13:34:29 +0100 Subject: [PATCH 33/43] Update CHANGELOG.md Added descriptions of DNA variant calling parameters --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc2325aa..447ad1be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Parameters * Added `--call_variants` to detect DNA variants -* Added `--split_mnps` to -* Added `--phase_vcf` to -* Added `--skip_medaka` to skip `medaka variant` +* Added `--split_mnps` to split multi-nucleotide polymorphisms into single nucleotide polymorphisms +* Added `--phase_vcf` to output a phased vcf +* Added `--skip_medaka` to skip `medaka_variant` * Added `--skip_sniffles` to skip `sniffles` ### Software dependencies From 46b92ef2615cc31254416b3f3c9fbb99c65970a3 Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Wed, 22 Dec 2021 22:59:10 +0100 Subject: [PATCH 34/43] Update test_nobc_nodx_vc.config Moved input to nfcore test-datasets --- conf/test_nobc_nodx_vc.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test_nobc_nodx_vc.config b/conf/test_nobc_nodx_vc.config index b8463f9a..98a1cda3 100644 --- a/conf/test_nobc_nodx_vc.config +++ b/conf/test_nobc_nodx_vc.config @@ -17,7 +17,7 @@ params { max_time = 12.h // Input data to skip basecalling and demultiplexing, and variant call - input = 'https://raw.githubusercontent.com//christopher-hakkaart/testdata/main/samplesheet_nobc_vc.csv' + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/nanoseq/samplesheet_nobc_vc.csv' protocol = 'DNA' skip_basecalling = true skip_quantification = true From 4e3dc8316c2a0d0c7337ea9411b2ccd33df1ce92 Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Thu, 23 Dec 2021 10:07:41 +0100 Subject: [PATCH 35/43] Update test_nobc_nodx_vc.config Fix late night typo --- conf/test_nobc_nodx_vc.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test_nobc_nodx_vc.config b/conf/test_nobc_nodx_vc.config index 98a1cda3..10c15217 100644 --- a/conf/test_nobc_nodx_vc.config +++ b/conf/test_nobc_nodx_vc.config @@ -17,7 +17,7 @@ params { max_time = 12.h // Input data to skip basecalling and demultiplexing, and variant call - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/nanoseq/samplesheet_nobc_vc.csv' + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/nanoseq/samplesheet_nobc_nodx_vc.csv' protocol = 'DNA' skip_basecalling = true skip_quantification = true From d49b7635e8d24c2e56ec4b285fb3dcc2c14859f8 Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Thu, 23 Dec 2021 12:25:17 +0100 Subject: [PATCH 36/43] Update multiqc.nf Update multiqc to fix conda error --- modules/local/multiqc.nf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/local/multiqc.nf b/modules/local/multiqc.nf index 44d8ca11..7f9cbfda 100644 --- a/modules/local/multiqc.nf +++ b/modules/local/multiqc.nf @@ -1,10 +1,10 @@ process MULTIQC { label 'process_medium' - conda (params.enable_conda ? "bioconda::multiqc=1.10.1" : null) + conda (params.enable_conda ? 'bioconda::multiqc=1.11' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.10.1--pyhdfd78af_1' : - 'quay.io/biocontainers/multiqc:1.10.1--pyhdfd78af_1' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0' }" input: path ch_multiqc_config From 4afd54c081ee004cc048dcc0810d95659e722ff4 Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Thu, 23 Dec 2021 12:29:46 +0100 Subject: [PATCH 37/43] Update CHANGELOG.md Update multiqc to fix conda error --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 447ad1be..e642ae5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Bump minimum Nextflow version from 21.04.0 -> 21.10.3 * Update pipeline template to nf-core/tools `2.2` * Update `bambu` version from `1.0.2` to `2.0.0` +* Update `multiqc` version from `1.10.1` to `1.11` ### Parameters @@ -29,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | `bioconductor-bambu` | 1.0.2 | 2.0.0 | | `bioconductor-bsgenome` | 1.58.0 | 1.62.0 | | `medaka` | | 1.4.4 | +| `multiqc` | 1.10.1 | 1.11 | | `sniffles` | | 1.0.12 | > **NB:** Dependency has been __updated__ if both old and new version information is present. From 93d02f483cd6150ad2f131d3b5c16ab8ee0c8af2 Mon Sep 17 00:00:00 2001 From: Chris Hakkaart Date: Thu, 23 Dec 2021 12:32:38 +0100 Subject: [PATCH 38/43] Update modules.config Fix output folder name and save sorted bam and bai --- conf/modules.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index b7cf744d..82716142 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -323,10 +323,10 @@ if (!params.skip_alignment) { } withName: SAMTOOLS_SORT_INDEX { publishDir = [ - path: { "${params.outdir}/graphmap2" }, + path: { "${params.outdir}/minimap2" }, mode: 'copy', enabled: true, - pattern: "*{'.sorted.bam','.bai'}", + pattern: "*.{sorted.bam,sorted.bam.bai}", saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } From f4b01d95b9ab662f0d5c0f03600b10075e7960c5 Mon Sep 17 00:00:00 2001 From: Venkat Malladi Date: Thu, 30 Dec 2021 15:21:25 -0600 Subject: [PATCH 39/43] Add search path for all files. --- modules/local/get_test_data.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/local/get_test_data.nf b/modules/local/get_test_data.nf index a0b8f606..1ebb9b57 100644 --- a/modules/local/get_test_data.nf +++ b/modules/local/get_test_data.nf @@ -1,8 +1,8 @@ process GET_TEST_DATA { container "docker.io/yuukiiwa/git:latest" - output: - path "test-datasets/fast5/$barcoded" , emit: ch_input_path + output: + path "test-datasets/fast5/$barcoded/*" , emit: ch_input_path script: barcoded = workflow.profile.contains('test_bc_nodx') ? "nonbarcoded" : "barcoded" From 8e08c728599c2487596ce857bb9e110a0d7553d9 Mon Sep 17 00:00:00 2001 From: Venkat Malladi Date: Thu, 30 Dec 2021 16:47:29 -0600 Subject: [PATCH 40/43] Add bug fix to change log. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e642ae5e..c881ae13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | `multiqc` | 1.10.1 | 1.11 | | `sniffles` | | 1.0.12 | +### Bug fix + +* The `GET_TEST_DATA` process now uses checks for any file in the path. + > **NB:** Dependency has been __updated__ if both old and new version information is present. > **NB:** Dependency has been __added__ if just the new version information is present. > **NB:** Dependency has been __removed__ if version information isn't present. From efab36bf144335a3ee2aa0e85e4b8f191907b106 Mon Sep 17 00:00:00 2001 From: Venkat Malladi Date: Mon, 3 Jan 2022 10:08:47 -0600 Subject: [PATCH 41/43] Fix path. --- modules/local/get_test_data.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/get_test_data.nf b/modules/local/get_test_data.nf index 1ebb9b57..90ccb93f 100644 --- a/modules/local/get_test_data.nf +++ b/modules/local/get_test_data.nf @@ -2,7 +2,7 @@ process GET_TEST_DATA { container "docker.io/yuukiiwa/git:latest" output: - path "test-datasets/fast5/$barcoded/*" , emit: ch_input_path + path "test-datasets/fast5/$barcoded/" , emit: ch_input_path script: barcoded = workflow.profile.contains('test_bc_nodx') ? "nonbarcoded" : "barcoded" From d7968965fc5fe1db3021c16b9b68fb88143ff20e Mon Sep 17 00:00:00 2001 From: Venkat Malladi Date: Tue, 4 Jan 2022 09:52:54 -0600 Subject: [PATCH 42/43] Make changes to path to meet azure requirements. --- modules/local/get_test_data.nf | 2 +- modules/local/guppy.nf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/local/get_test_data.nf b/modules/local/get_test_data.nf index 90ccb93f..1ebb9b57 100644 --- a/modules/local/get_test_data.nf +++ b/modules/local/get_test_data.nf @@ -2,7 +2,7 @@ process GET_TEST_DATA { container "docker.io/yuukiiwa/git:latest" output: - path "test-datasets/fast5/$barcoded/" , emit: ch_input_path + path "test-datasets/fast5/$barcoded/*" , emit: ch_input_path script: barcoded = workflow.profile.contains('test_bc_nodx') ? "nonbarcoded" : "barcoded" diff --git a/modules/local/guppy.nf b/modules/local/guppy.nf index 4d839923..d7041af6 100644 --- a/modules/local/guppy.nf +++ b/modules/local/guppy.nf @@ -9,7 +9,7 @@ process GUPPY { } input: - path(input_path) + path(input_path), stageAs: 'input_path/*' val meta path guppy_config path guppy_model @@ -30,7 +30,7 @@ process GUPPY { if (params.guppy_model) model = file(params.guppy_model).exists() ? "--model ./$guppy_model" : "--model $params.guppy_model" """ guppy_basecaller \\ - --input_path $input_path \\ + --input_path input_path \\ --save_path ./basecalling \\ --records_per_fastq 0 \\ --compress_fastq \\ From e4fe8866a40eb3c4bf8041d847d4d15d55a64e9f Mon Sep 17 00:00:00 2001 From: Venkat Malladi Date: Tue, 4 Jan 2022 10:20:35 -0600 Subject: [PATCH 43/43] Update schema to be file list instead of just directory. --- nextflow_schema.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 60dce87b..806e123e 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -63,8 +63,9 @@ "properties": { "input_path": { "type": "string", - "description": "Path to Nanopore run directory (e.g. 'fastq_pass/') or a basecalled fastq file that requires demultiplexing. The latter can only be provided in conjunction with the '--skip_basecalling' parameter.", - "fa_icon": "fas fa-folder-open" + "format": "file-path", + "description": "Path to Nanopore run directory files (e.g. 'fastq_pass/*') or a basecalled fastq file that requires demultiplexing. The latter can only be provided in conjunction with the '--skip_basecalling' parameter.", + "fa_icon": "far fa-file-code" }, "flowcell": { "type": "string",