From 6966552a235813dd4c898dc41e72f6e0b5ceb19d Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 1 Sep 2023 16:10:18 +0200 Subject: [PATCH 001/175] remove check for filter in favor of direct use of tools_cutoff parameter --- conf/modules.config | 1 - modules/local/fusionreport/detect/main.nf | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 4bee29b3..b5ba9bdc 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -99,7 +99,6 @@ process { withName: FUSIONREPORT { ext.when = { !params.skip_vis } ext.args = "--export csv" - ext.args2 = { params.fusionreport_filter ? "--tool-cutoff 2" : "--tool-cutoff 1"} publishDir = [ path: { "${params.outdir}/fusionreport/${meta.id}" }, mode: params.publish_dir_mode, diff --git a/modules/local/fusionreport/detect/main.nf b/modules/local/fusionreport/detect/main.nf index 0b18c34a..7723ecc5 100644 --- a/modules/local/fusionreport/detect/main.nf +++ b/modules/local/fusionreport/detect/main.nf @@ -10,6 +10,7 @@ process FUSIONREPORT { input: tuple val(meta), path(reads), path(arriba_fusions), path(pizzly_fusions), path(squid_fusions), path(starfusion_fusions), path(fusioncatcher_fusions) tuple val(meta2), path(fusionreport_ref) + val(tools_cutoff) output: path "versions.yml" , emit: versions @@ -33,7 +34,7 @@ process FUSIONREPORT { tools += params.fusioncatcher || params.all ? "--fusioncatcher ${fusioncatcher_fusions} " : '' def prefix = task.ext.prefix ?: "${meta.id}" """ - fusion_report run $meta.id . $fusionreport_ref $tools --allow-multiple-gene-symbols $args $args2 + fusion_report run $meta.id . $fusionreport_ref $tools --allow-multiple-gene-symbols $tools_cutoff $args $args2 mv fusion_list.tsv ${prefix}.fusionreport.tsv mv fusion_list_filtered.tsv ${prefix}.fusionreport_filtered.tsv From 0a574c2f0c2e56244a8b7ceebea44dc125faa48f Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 1 Sep 2023 16:11:34 +0200 Subject: [PATCH 002/175] filters -> tools_cutoff, and adapt documentation --- docs/usage.md | 8 ++-- nextflow.config | 3 +- nextflow_schema.json | 46 ++++++++----------- .../local/fusioninspector_workflow.nf | 7 ++- subworkflows/local/fusionreport_workflow.nf | 2 +- 5 files changed, 28 insertions(+), 38 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 48bb9e10..155aa80c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -164,7 +164,7 @@ nextflow run nf-core/rnafusion \ --trim ``` -#### Filter fusions detected by 2 or more tools +#### Filter out fusions detected by less than INT tools ```bash nextflow run nf-core/rnafusion \ @@ -172,12 +172,10 @@ nextflow run nf-core/rnafusion \ --input \ --genomes_base \ --outdir - --fusioninspector_filter - --fusionreport_filter + --tools_cutoff ``` -`--fusioninspector_filter` feed only fusions detected by 2 or more tools to fusioninspector for closer analysis (false by default). -`--fusionreport_filter` displays only fusions detected by 2 or more tools in fusionreport html index (true by default). +`--tools_cutoff INT` will discard fusions detected by less than INT tools both for display in fusionreport html index and to consider in fusioninspector. #### Adding custom fusions to consider as well as the detected set: whitelist diff --git a/nextflow.config b/nextflow.config index 107eba8a..3034c3e3 100644 --- a/nextflow.config +++ b/nextflow.config @@ -32,8 +32,7 @@ params { starfusion_build = true // Filtering - fusioninspector_filter = false - fusionreport_filter = true + tools_cutoff = 1 // Trimming trim = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 02cd1e27..631734b0 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -62,6 +62,16 @@ "fa_icon": "far fa-file-code", "description": "Specifies which analysis type for the pipeline - either build references or analyse data" }, + "cosmic_username": { + "type": "string", + "fa_icon": "far fa-file-code", + "description": "COSMIC username" + }, + "cosmic_passwd": { + "type": "string", + "fa_icon": "far fa-file-code", + "description": "COSMIC password" + }, "genomes_base": { "type": "string", "fa_icon": "far fa-file-code", @@ -149,11 +159,6 @@ "fa_icon": "far fa-file-code", "description": "Path to fusioncatcher references" }, - "fusioninspector_filter": { - "type": "boolean", - "fa_icon": "far fa-file-code", - "description": "Feed filtered fusionreport fusions to fusioninspector" - }, "fusioninspector_limitSjdbInsertNsj": { "type": "integer", "fa_icon": "far fa-file-code", @@ -179,12 +184,6 @@ "fa_icon": "far fa-file-code", "description": "Path to fusionreport references" }, - "fusionreport_filter": { - "type": "boolean", - "fa_icon": "far fa-file-code", - "default": true, - "description": "Display fusions identified with 2 tools or more" - }, "pizzly": { "type": "boolean", "fa_icon": "far fa-file-code", @@ -200,6 +199,11 @@ "fa_icon": "far fa-file-code", "description": "Path to pizzly references" }, + "qiagen": { + "type": "boolean", + "fa_icon": "far fa-file-code", + "description": "Use QIAGEN instead of SANGER to download COSMIC database" + }, "squid": { "type": "boolean", "fa_icon": "far fa-file-code", @@ -245,25 +249,15 @@ "fa_icon": "far fa-file-code", "description": "Run stringtie analysis" }, - "whitelist": { - "type": "string", - "fa_icon": "far fa-file-code", - "description": "Path to fusions to add to the input of fusioninspector" - }, - "cosmic_username": { - "type": "string", + "tools_cutoff": { + "type": "integer", "fa_icon": "far fa-file-code", - "description": "COSMIC username" + "description": "Discard fusions identified by less than INT tools" }, - "cosmic_passwd": { + "whitelist": { "type": "string", "fa_icon": "far fa-file-code", - "description": "COSMIC password" - }, - "qiagen": { - "type": "boolean", - "fa_icon": "far fa-file-code", - "description": "Use QIAGEN instead of SANGER to download COSMIC database" + "description": "Path to fusions to add to the input of fusioninspector" } } }, diff --git a/subworkflows/local/fusioninspector_workflow.nf b/subworkflows/local/fusioninspector_workflow.nf index 3c224689..2f1de87e 100644 --- a/subworkflows/local/fusioninspector_workflow.nf +++ b/subworkflows/local/fusioninspector_workflow.nf @@ -17,19 +17,18 @@ workflow FUSIONINSPECTOR_WORKFLOW { main: ch_versions = Channel.empty() index ="${params.starfusion_ref}" - ch_fusion_list = params.fusioninspector_filter ? fusion_list_filtered : fusion_list if (params.whitelist) { - ch_whitelist = ch_fusion_list.combine(Channel.value(file(params.whitelist, checkIfExists:true))) + ch_whitelist = fusion_list_filtered.combine(Channel.value(file(params.whitelist, checkIfExists:true))) .map { meta, fusions, whitelist -> [ meta, [fusions, whitelist] ] } CAT_CAT(ch_whitelist) // fusioninspector takes care of possible duplicates ch_versions = ch_versions.mix(CAT_CAT.out.versions) - ch_fusion_list = CAT_CAT.out.file_out + fusion_list_filtered = CAT_CAT.out.file_out } - reads_fusion = reads.join(ch_fusion_list ) + reads_fusion = reads.join(fusion_list_filtered ) FUSIONINSPECTOR( reads_fusion, index) ch_versions = ch_versions.mix(FUSIONINSPECTOR.out.versions) diff --git a/subworkflows/local/fusionreport_workflow.nf b/subworkflows/local/fusionreport_workflow.nf index 478986a4..c2bcf201 100644 --- a/subworkflows/local/fusionreport_workflow.nf +++ b/subworkflows/local/fusionreport_workflow.nf @@ -23,7 +23,7 @@ workflow FUSIONREPORT_WORKFLOW { .join(starfusion_fusions, remainder: true) .join(fusioncatcher_fusions, remainder: true) - FUSIONREPORT(reads_fusions, fusionreport_ref) + FUSIONREPORT(reads_fusions, fusionreport_ref, params.tools_cutoff) ch_fusion_list = FUSIONREPORT.out.fusion_list ch_fusion_list_filtered = FUSIONREPORT.out.fusion_list_filtered ch_versions = ch_versions.mix(FUSIONREPORT.out.versions) From bec1a763fb74f79ec55517862d8934f43e920ffb Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 1 Sep 2023 18:03:14 +0200 Subject: [PATCH 003/175] add actual argument for tools-cutoff in fusionreport --- modules/local/fusionreport/detect/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/fusionreport/detect/main.nf b/modules/local/fusionreport/detect/main.nf index 7723ecc5..6b5cbf9c 100644 --- a/modules/local/fusionreport/detect/main.nf +++ b/modules/local/fusionreport/detect/main.nf @@ -34,7 +34,7 @@ process FUSIONREPORT { tools += params.fusioncatcher || params.all ? "--fusioncatcher ${fusioncatcher_fusions} " : '' def prefix = task.ext.prefix ?: "${meta.id}" """ - fusion_report run $meta.id . $fusionreport_ref $tools --allow-multiple-gene-symbols $tools_cutoff $args $args2 + fusion_report run $meta.id . $fusionreport_ref $tools --allow-multiple-gene-symbols --tool-cutoff $tools_cutoff $args $args2 mv fusion_list.tsv ${prefix}.fusionreport.tsv mv fusion_list_filtered.tsv ${prefix}.fusionreport_filtered.tsv From 961b70dc71706d91e09b8e277bff246b8369eac8 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:26:23 +0200 Subject: [PATCH 004/175] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e57f2ea6..a0d38753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use institutional configs by default [#381](https://github.com/nf-core/rnafusion/pull/381) - Remove redundant indexing in starfusion and qc workflows [#387](https://github.com/nf-core/rnafusion/pull/387) - Output bai files in same directory as bam files [#387](https://github.com/nf-core/rnafusion/pull/387) +- Removed `--fusioninspector_filter` and `--fusionreport_filter` in favor of `--tools_cutoff` (default = 1, no filters applied) [#389](https://github.com/nf-core/rnafusion/pull/389) ### Fixed From 491501c9f6ac97f518ab4a46034581869e8546aa Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 6 Sep 2023 10:43:09 +0200 Subject: [PATCH 005/175] add validation of tools_cutoff values --- workflows/rnafusion.nf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/workflows/rnafusion.nf b/workflows/rnafusion.nf index 1fbaee3e..49e60056 100644 --- a/workflows/rnafusion.nf +++ b/workflows/rnafusion.nf @@ -19,6 +19,8 @@ WorkflowRnafusion.initialise(params, log) if (file(params.input).exists() || params.build_references) { ch_input = file(params.input) } else { exit 1, 'Input samplesheet does not exist or was not specified!' } if (params.fusioninspector_only && !params.fusioninspector_fusions) { exit 1, 'Parameter --fusioninspector_fusions PATH_TO_FUSION_LIST expected with parameter --fusioninspector_only'} +if (params.tools_cutoff < 1) { exit 1, 'Parameter: --tools_cutoff should be >= 1'} + ch_chrgtf = params.starfusion_build ? Channel.fromPath(params.chrgtf).map { it -> [[id:it.Name], it] }.collect() : Channel.fromPath("${params.starfusion_ref}/ref_annot.gtf").map { it -> [[id:it.Name], it] }.collect() ch_starindex_ref = params.starfusion_build ? Channel.fromPath(params.starindex_ref).map { it -> [[id:it.Name], it] }.collect() : Channel.fromPath("${params.starfusion_ref}/ref_genome.fa.star.idx").map { it -> [[id:it.Name], it] }.collect() From 9abda97bd3fe65176a8e2b59c381801a3d280219 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 6 Sep 2023 10:44:22 +0200 Subject: [PATCH 006/175] refactor channel names --- subworkflows/local/fusioninspector_workflow.nf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/subworkflows/local/fusioninspector_workflow.nf b/subworkflows/local/fusioninspector_workflow.nf index 4d60bfae..733858fb 100644 --- a/subworkflows/local/fusioninspector_workflow.nf +++ b/subworkflows/local/fusioninspector_workflow.nf @@ -34,18 +34,18 @@ workflow FUSIONINSPECTOR_WORKFLOW { ch_fusion_list.fusions = CAT_CAT.out.file_out } - reads_fusion = reads.join(ch_fusion_list.fusions ) + ch_reads_fusion = reads.join(ch_fusion_list.fusions ) - FUSIONINSPECTOR( reads_fusion, index) + FUSIONINSPECTOR( ch_reads_fusion, index) ch_versions = ch_versions.mix(FUSIONINSPECTOR.out.versions) - fusion_data = FUSIONINSPECTOR.out.tsv.join(report) - MEGAFUSION(fusion_data) + ch_fusion_data = FUSIONINSPECTOR.out.tsv.join(report) + MEGAFUSION(ch_fusion_data) ch_versions = ch_versions.mix(MEGAFUSION.out.versions) if ((params.starfusion || params.all || params.stringtie) && !params.fusioninspector_only && !params.skip_vis) { - bam_sorted_indexed_fusions = bam_sorted_indexed.join(FUSIONINSPECTOR.out.tsv) - ARRIBA_VISUALISATION(bam_sorted_indexed_fusions, ch_gtf, ch_arriba_ref_protein_domains, ch_arriba_ref_cytobands) + ch_bam_sorted_indexed_fusions = bam_sorted_indexed.join(FUSIONINSPECTOR.out.tsv) + ARRIBA_VISUALISATION(ch_bam_sorted_indexed_fusions, ch_gtf, ch_arriba_ref_protein_domains, ch_arriba_ref_cytobands) ch_versions = ch_versions.mix(ARRIBA_VISUALISATION.out.versions) } From 7118fe979740fd1b96bde80aee2c9cc9e028665a Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:51:28 +0200 Subject: [PATCH 007/175] replace picard/markduplicates module with gatk4/markduplicates --- conf/modules.config | 7 +- modules.json | 10 +-- modules/nf-core/gatk4/markduplicates/main.nf | 68 +++++++++++++++++ modules/nf-core/gatk4/markduplicates/meta.yml | 73 +++++++++++++++++++ modules/nf-core/picard/markduplicates/main.nf | 65 ----------------- .../nf-core/picard/markduplicates/meta.yml | 71 ------------------ subworkflows/local/qc_workflow.nf | 8 +- tower.yml | 2 +- 8 files changed, 157 insertions(+), 147 deletions(-) create mode 100644 modules/nf-core/gatk4/markduplicates/main.nf create mode 100644 modules/nf-core/gatk4/markduplicates/meta.yml delete mode 100644 modules/nf-core/picard/markduplicates/main.nf delete mode 100644 modules/nf-core/picard/markduplicates/meta.yml diff --git a/conf/modules.config b/conf/modules.config index b4dc96d6..f8ab151e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -157,8 +157,13 @@ process { } - withName: PICARD_MARKDUPLICATES { + withName: GATK4_MARKDUPLICATES { ext.when = { !params.skip_qc && !params.fusioninspector_only && (params.starfusion || params.all) } + publishDir = [ + path: { "${params.outdir}/picard" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + ] } withName: PIZZLY { diff --git a/modules.json b/modules.json index 162a71b5..c1686270 100644 --- a/modules.json +++ b/modules.json @@ -45,6 +45,11 @@ "git_sha": "541811d779026c5d395925895fa5ed35e7216cc0", "installed_by": ["modules"] }, + "gatk4/markduplicates": { + "branch": "master", + "git_sha": "cf8f9ace77aac01caa5c7cb92af5bbda7adb77bd", + "installed_by": ["modules"] + }, "kallisto/index": { "branch": "master", "git_sha": "699fa6f3002d922380615f3847198aeb57d8b6a9", @@ -60,11 +65,6 @@ "git_sha": "735e1e04e7e01751d2d6e97055bbdb6f70683cc1", "installed_by": ["modules"] }, - "picard/markduplicates": { - "branch": "master", - "git_sha": "2ee934606f1fdf7fc1cb05d6e8abc13bec8ab448", - "installed_by": ["modules"] - }, "qualimap/rnaseq": { "branch": "master", "git_sha": "4657d98bc9f565e067c4d924126ce107056f5e2f", diff --git a/modules/nf-core/gatk4/markduplicates/main.nf b/modules/nf-core/gatk4/markduplicates/main.nf new file mode 100644 index 00000000..e4c01f9a --- /dev/null +++ b/modules/nf-core/gatk4/markduplicates/main.nf @@ -0,0 +1,68 @@ +process GATK4_MARKDUPLICATES { + tag "$meta.id" + label 'process_medium' + + conda "bioconda::gatk4=4.4.0.0 bioconda::samtools=1.17" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-d9e7bad0f7fbc8f4458d5c3ab7ffaaf0235b59fb:f857e2d6cc88d35580d01cf39e0959a68b83c1d9-0': + 'biocontainers/mulled-v2-d9e7bad0f7fbc8f4458d5c3ab7ffaaf0235b59fb:f857e2d6cc88d35580d01cf39e0959a68b83c1d9-0' }" + + input: + tuple val(meta), path(bam) + path fasta + path fasta_fai + + output: + tuple val(meta), path("*cram"), emit: cram, optional: true + tuple val(meta), path("*bam"), emit: bam, optional: true + tuple val(meta), path("*.crai"), emit: crai, optional: true + tuple val(meta), path("*.bai"), emit: bai, optional: true + tuple val(meta), path("*.metrics"), emit: metrics + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}.bam" + + // If the extension is CRAM, then change it to BAM + prefix_bam = prefix.tokenize('.')[-1] == 'cram' ? "${prefix.substring(0, prefix.lastIndexOf('.'))}.bam" : prefix + + def input_list = bam.collect{"--INPUT $it"}.join(' ') + def reference = fasta ? "--REFERENCE_SEQUENCE ${fasta}" : "" + + def avail_mem = 3072 + if (!task.memory) { + log.info '[GATK MarkDuplicates] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.' + } else { + avail_mem = (task.memory.mega*0.8).intValue() + } + + // Using samtools and not Markduplicates to compress to CRAM speeds up computation: + // https://medium.com/@acarroll.dna/looking-at-trade-offs-in-compression-levels-for-genomics-tools-eec2834e8b94 + """ + gatk --java-options "-Xmx${avail_mem}M -XX:-UsePerfData" \\ + MarkDuplicates \\ + $input_list \\ + --OUTPUT ${prefix_bam} \\ + --METRICS_FILE ${prefix}.metrics \\ + --TMP_DIR . \\ + ${reference} \\ + $args + + # If cram files are wished as output, the run samtools for conversion + if [[ ${prefix} == *.cram ]]; then + samtools view -Ch -T ${fasta} -o ${prefix} ${prefix_bam} + rm ${prefix_bam} + samtools index ${prefix} + fi + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + gatk4: \$(echo \$(gatk --version 2>&1) | sed 's/^.*(GATK) v//; s/ .*\$//') + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/gatk4/markduplicates/meta.yml b/modules/nf-core/gatk4/markduplicates/meta.yml new file mode 100644 index 00000000..d3e75505 --- /dev/null +++ b/modules/nf-core/gatk4/markduplicates/meta.yml @@ -0,0 +1,73 @@ +name: gatk4_markduplicates +description: This tool locates and tags duplicate reads in a BAM or SAM file, where duplicate reads are defined as originating from a single fragment of DNA. +keywords: + - bam + - gatk4 + - markduplicates + - sort +tools: + - gatk4: + description: + Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools + with a primary focus on variant discovery and genotyping. Its powerful processing engine + and high-performance computing features make it capable of taking on projects of any size. + homepage: https://gatk.broadinstitute.org/hc/en-us + documentation: https://gatk.broadinstitute.org/hc/en-us/articles/360037052812-MarkDuplicates-Picard- + tool_dev_url: https://github.com/broadinstitute/gatk + doi: 10.1158/1538-7445.AM2017-3590 + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - bam: + type: file + description: Sorted BAM file + pattern: "*.{bam}" + - fasta: + type: file + description: Fasta file + pattern: "*.{fasta}" + - fasta_fai: + type: file + description: Fasta index file + pattern: "*.{fai}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - bam: + type: file + description: Marked duplicates BAM file + pattern: "*.{bam}" + - cram: + type: file + description: Marked duplicates CRAM file + pattern: "*.{cram}" + - bai: + type: file + description: BAM index file + pattern: "*.{bam.bai}" + - crai: + type: file + description: CRAM index file + pattern: "*.{cram.crai}" + - metrics: + type: file + description: Duplicate metrics file generated by GATK + pattern: "*.{metrics.txt}" + +authors: + - "@ajodeh-juma" + - "@FriederikeHanssen" + - "@maxulysse" diff --git a/modules/nf-core/picard/markduplicates/main.nf b/modules/nf-core/picard/markduplicates/main.nf deleted file mode 100644 index ebfa0864..00000000 --- a/modules/nf-core/picard/markduplicates/main.nf +++ /dev/null @@ -1,65 +0,0 @@ -process PICARD_MARKDUPLICATES { - tag "$meta.id" - label 'process_medium' - - conda "bioconda::picard=3.0.0" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/picard:3.0.0--hdfd78af_1' : - 'biocontainers/picard:3.0.0--hdfd78af_1' }" - - input: - tuple val(meta), path(bam) - tuple val(meta2), path(fasta) - tuple val(meta3), path(fai) - - output: - tuple val(meta), path("*.bam") , emit: bam - tuple val(meta), path("*.bai") , optional:true, emit: bai - tuple val(meta), path("*.metrics.txt"), emit: metrics - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - def avail_mem = 3072 - if (!task.memory) { - log.info '[Picard MarkDuplicates] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.' - } else { - avail_mem = (task.memory.mega*0.8).intValue() - } - - if ("$bam" == "${prefix}.bam") error "Input and output names are the same, use \"task.ext.prefix\" to disambiguate!" - - """ - picard \\ - -Xmx${avail_mem}M \\ - MarkDuplicates \\ - $args \\ - --INPUT $bam \\ - --OUTPUT ${prefix}.bam \\ - --REFERENCE_SEQUENCE $fasta \\ - --METRICS_FILE ${prefix}.MarkDuplicates.metrics.txt - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - picard: \$(echo \$(picard MarkDuplicates --version 2>&1) | grep -o 'Version:.*' | cut -f2- -d:) - END_VERSIONS - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - if ("$bam" == "${prefix}.bam") error "Input and output names are the same, use \"task.ext.prefix\" to disambiguate!" - """ - touch ${prefix}.bam - touch ${prefix}.bam.bai - touch ${prefix}.MarkDuplicates.metrics.txt - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - picard: \$(echo \$(picard MarkDuplicates --version 2>&1) | grep -o 'Version:.*' | cut -f2- -d:) - END_VERSIONS - """ -} diff --git a/modules/nf-core/picard/markduplicates/meta.yml b/modules/nf-core/picard/markduplicates/meta.yml deleted file mode 100644 index f7693d2f..00000000 --- a/modules/nf-core/picard/markduplicates/meta.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: picard_markduplicates -description: Locate and tag duplicate reads in a BAM file -keywords: - - markduplicates - - pcr - - duplicates - - bam - - sam - - cram -tools: - - picard: - description: | - A set of command line tools (in Java) for manipulating high-throughput sequencing (HTS) - data and formats such as SAM/BAM/CRAM and VCF. - homepage: https://broadinstitute.github.io/picard/ - documentation: https://broadinstitute.github.io/picard/ - licence: ["MIT"] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM file - pattern: "*.{bam,cram,sam}" - - meta2: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'genome' ] - - fasta: - type: file - description: Reference genome fasta file - pattern: "*.{fasta,fa}" - - meta3: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'genome' ] - - fai: - type: file - description: Reference genome fasta index - pattern: "*.{fai}" -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM file with duplicate reads marked/removed - pattern: "*.{bam}" - - bai: - type: file - description: An optional BAM index file. If desired, --CREATE_INDEX must be passed as a flag - pattern: "*.{bai}" - - metrics: - type: file - description: Duplicate metrics file generated by picard - pattern: "*.{metrics.txt}" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@drpatelh" - - "@projectoriented" - - "@ramprasadn" diff --git a/subworkflows/local/qc_workflow.nf b/subworkflows/local/qc_workflow.nf index bdf887d1..d9cd0a87 100644 --- a/subworkflows/local/qc_workflow.nf +++ b/subworkflows/local/qc_workflow.nf @@ -4,7 +4,7 @@ include { QUALIMAP_RNASEQ } from '../../modules/nf-core/qualimap/rnaseq/main' include { PICARD_COLLECTRNASEQMETRICS } from '../../modules/local/picard/collectrnaseqmetrics/main' -include { PICARD_MARKDUPLICATES } from '../../modules/nf-core/picard/markduplicates/main' +include { GATK4_MARKDUPLICATES } from '../../modules/nf-core/gatk4/markduplicates/main' workflow QC_WORKFLOW { take: @@ -27,9 +27,9 @@ workflow QC_WORKFLOW { ch_versions = ch_versions.mix(PICARD_COLLECTRNASEQMETRICS.out.versions) ch_rnaseq_metrics = Channel.empty().mix(PICARD_COLLECTRNASEQMETRICS.out.metrics) - PICARD_MARKDUPLICATES(ch_bam_sorted, ch_fasta, ch_fai) - ch_versions = ch_versions.mix(PICARD_MARKDUPLICATES.out.versions) - ch_duplicate_metrics = Channel.empty().mix(PICARD_MARKDUPLICATES.out.metrics) + GATK4_MARKDUPLICATES(ch_bam_sorted, ch_fasta, ch_fai) + ch_versions = ch_versions.mix(GATK4_MARKDUPLICATES.out.versions) + ch_duplicate_metrics = Channel.empty().mix(GATK4_MARKDUPLICATES.out.metrics) emit: diff --git a/tower.yml b/tower.yml index 5813f5d3..694a83ee 100644 --- a/tower.yml +++ b/tower.yml @@ -15,7 +15,7 @@ reports: display: "Fusion-report HTML report" "**/megafusion/*_fusion_data.vcf": display: "Collected statistics on each fusion fed to FusionInspector in VCF format" - "**/picard/*.MarkDuplicates.metrics.txt": + "**/gatk4/*.MarkDuplicates.metrics.txt": display: "Picard: Metrics from CollectRnaMetrics" "**/picard/*_rna_metrics.txt": display: "Picard: Metrics from MarkDuplicates" From 4c4f5002d7036df683c365906aa26616be70f801 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:31:58 +0200 Subject: [PATCH 008/175] add insertsizemetrics to QC workflow --- modules.json | 5 ++ .../picard/collectinsertsizemetrics/main.nf | 61 +++++++++++++++++++ .../picard/collectinsertsizemetrics/meta.yml | 48 +++++++++++++++ subworkflows/local/qc_workflow.nf | 5 ++ tower.yml | 6 +- workflows/rnafusion.nf | 1 + 6 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 modules/nf-core/picard/collectinsertsizemetrics/main.nf create mode 100644 modules/nf-core/picard/collectinsertsizemetrics/meta.yml diff --git a/modules.json b/modules.json index 162a71b5..a97f519a 100644 --- a/modules.json +++ b/modules.json @@ -55,6 +55,11 @@ "git_sha": "a6e11ac655e744f7ebc724be669dd568ffdc0e80", "installed_by": ["modules"] }, + "picard/collectinsertsizemetrics": { + "branch": "master", + "git_sha": "240937a2a9c30298110753292be041188891f2cb", + "installed_by": ["modules"] + }, "picard/collectwgsmetrics": { "branch": "master", "git_sha": "735e1e04e7e01751d2d6e97055bbdb6f70683cc1", diff --git a/modules/nf-core/picard/collectinsertsizemetrics/main.nf b/modules/nf-core/picard/collectinsertsizemetrics/main.nf new file mode 100644 index 00000000..1d538fae --- /dev/null +++ b/modules/nf-core/picard/collectinsertsizemetrics/main.nf @@ -0,0 +1,61 @@ +process PICARD_COLLECTINSERTSIZEMETRICS { + tag "$meta.id" + label 'process_single' + + conda "bioconda::picard=3.0.0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/picard:3.0.0--hdfd78af_1' : + 'biocontainers/picard:3.0.0--hdfd78af_1' }" + + input: + tuple val(meta), path(bam) + + output: + tuple val(meta), path("*.txt"), emit: metrics + tuple val(meta), path("*.pdf"), emit: histogram + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + def avail_mem = 3072 + if (!task.memory) { + log.info '[Picard CollectInsertSizeMetrics] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.' + } else { + avail_mem = (task.memory.mega*0.8).intValue() + } + """ + picard \\ + -Xmx${avail_mem}M \\ + CollectInsertSizeMetrics \\ + $args \\ + --INPUT $bam \\ + --OUTPUT ${prefix}.txt \\ + --Histogram_FILE ${prefix}.pdf \\ + $args + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + picard: \$(picard CollectInsertSizeMetrics --version 2>&1 | grep -o 'Version:.*' | cut -f2- -d:) + END_VERSIONS + """ + + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.pdf + touch ${prefix}.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + picard: \$(picard CollectInsertSizeMetrics --version 2>&1 | grep -o 'Version:.*' | cut -f2- -d:) + END_VERSIONS + """ + + + +} diff --git a/modules/nf-core/picard/collectinsertsizemetrics/meta.yml b/modules/nf-core/picard/collectinsertsizemetrics/meta.yml new file mode 100644 index 00000000..e611bdd4 --- /dev/null +++ b/modules/nf-core/picard/collectinsertsizemetrics/meta.yml @@ -0,0 +1,48 @@ +name: "picard_collectinsertsizemetrics" +description: Collect metrics about the insert size distribution of a paired-end library. +keywords: + - metrics + - alignment + - insert + - statistics + - bam + +tools: + - "picard": + description: "Java tools for working with NGS data in the BAM format" + homepage: "https://broadinstitute.github.io/picard/" + documentation: "https://broadinstitute.github.io/picard/" + tool_dev_url: "https://github.com/broadinstitute/picard" + 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}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - pdf: + type: file + description: Histogram plots of the insert size metrics computed by Picard + pattern: "*.pdf" + - metrics: + type: file + description: Values used by Picard to generate the insert size histograms + pattern: "*.txt" +authors: + - "@FerriolCalvet" diff --git a/subworkflows/local/qc_workflow.nf b/subworkflows/local/qc_workflow.nf index bdf887d1..b933a8be 100644 --- a/subworkflows/local/qc_workflow.nf +++ b/subworkflows/local/qc_workflow.nf @@ -31,12 +31,17 @@ workflow QC_WORKFLOW { ch_versions = ch_versions.mix(PICARD_MARKDUPLICATES.out.versions) ch_duplicate_metrics = Channel.empty().mix(PICARD_MARKDUPLICATES.out.metrics) + PICARD_COLLECTINSERTSIZEMETRICS(ch_bam_sorted) + ch_versions = ch_versions.mix(PICARD_COLLECTINSERTSIZEMETRICS.out.versions) + ch_insertsize_metrics = Channel.empty().mix(PICARD_COLLECTINSERTSIZEMETRICS.out.metrics) + emit: versions = ch_versions.ifEmpty(null) qualimap_qc = ch_qualimap_qc rnaseq_metrics = ch_rnaseq_metrics duplicate_metrics = ch_duplicate_metrics + insertsize_metrics = ch_insertsize_metrics } diff --git a/tower.yml b/tower.yml index 5813f5d3..7160ccb7 100644 --- a/tower.yml +++ b/tower.yml @@ -18,7 +18,11 @@ reports: "**/picard/*.MarkDuplicates.metrics.txt": display: "Picard: Metrics from CollectRnaMetrics" "**/picard/*_rna_metrics.txt": - display: "Picard: Metrics from MarkDuplicates" + display: "GATK4: Metrics from MarkDuplicates" + "**/picard/*insert*size*metrics.txt": + display: "GATK4: Metrics from InsertSizeMetrics" + "**/picard/*pdf": + display: "GATK4: InsertSizeMetrics histogram" "**/pizzly/*.pizzly.txt": display: "Pizzly identified fusion TXT report" "**/qualimap/qualimapReport.html": diff --git a/workflows/rnafusion.nf b/workflows/rnafusion.nf index 6f498e11..272e2f63 100644 --- a/workflows/rnafusion.nf +++ b/workflows/rnafusion.nf @@ -288,6 +288,7 @@ workflow RNAFUSION { ch_multiqc_files = ch_multiqc_files.mix(STARFUSION_WORKFLOW.out.star_stats.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.rnaseq_metrics.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.duplicate_metrics.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.insertsize_metrics.collect{it[1]}.ifEmpty([])) From 035bcfbef5acca1fc2ba43bab742997b4b5e4d79 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 22 Sep 2023 15:55:47 +0200 Subject: [PATCH 009/175] replace qualimap/rnaseq with mosdepth --- modules.json | 10 +-- modules/nf-core/mosdepth/main.nf | 80 +++++++++++++++++ modules/nf-core/mosdepth/meta.yml | 109 +++++++++++++++++++++++ modules/nf-core/qualimap/rnaseq/main.nf | 63 ------------- modules/nf-core/qualimap/rnaseq/meta.yml | 52 ----------- subworkflows/local/qc_workflow.nf | 17 ++-- tower.yml | 2 + workflows/rnafusion.nf | 3 +- 8 files changed, 210 insertions(+), 126 deletions(-) create mode 100644 modules/nf-core/mosdepth/main.nf create mode 100644 modules/nf-core/mosdepth/meta.yml delete mode 100644 modules/nf-core/qualimap/rnaseq/main.nf delete mode 100644 modules/nf-core/qualimap/rnaseq/meta.yml diff --git a/modules.json b/modules.json index 162a71b5..fe82c5aa 100644 --- a/modules.json +++ b/modules.json @@ -50,6 +50,11 @@ "git_sha": "699fa6f3002d922380615f3847198aeb57d8b6a9", "installed_by": ["modules"] }, + "mosdepth": { + "branch": "master", + "git_sha": "ebb27711cd5f4de921244bfa81c676504072d31c", + "installed_by": ["modules"] + }, "multiqc": { "branch": "master", "git_sha": "a6e11ac655e744f7ebc724be669dd568ffdc0e80", @@ -65,11 +70,6 @@ "git_sha": "2ee934606f1fdf7fc1cb05d6e8abc13bec8ab448", "installed_by": ["modules"] }, - "qualimap/rnaseq": { - "branch": "master", - "git_sha": "4657d98bc9f565e067c4d924126ce107056f5e2f", - "installed_by": ["modules"] - }, "samtools/faidx": { "branch": "master", "git_sha": "fd742419940e01ba1c5ecb172c3e32ec840662fe", diff --git a/modules/nf-core/mosdepth/main.nf b/modules/nf-core/mosdepth/main.nf new file mode 100644 index 00000000..74db3a27 --- /dev/null +++ b/modules/nf-core/mosdepth/main.nf @@ -0,0 +1,80 @@ +process MOSDEPTH { + tag "$meta.id" + label 'process_medium' + + conda "bioconda::mosdepth=0.3.3" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mosdepth:0.3.3--hdfd78af_1' : + 'biocontainers/mosdepth:0.3.3--hdfd78af_1'}" + + input: + tuple val(meta), path(bam), path(bai), path(bed) + tuple val(meta2), path(fasta) + + output: + tuple val(meta), path('*.global.dist.txt') , emit: global_txt + tuple val(meta), path('*.summary.txt') , emit: summary_txt + tuple val(meta), path('*.region.dist.txt') , optional:true, emit: regions_txt + tuple val(meta), path('*.per-base.d4') , optional:true, emit: per_base_d4 + tuple val(meta), path('*.per-base.bed.gz') , optional:true, emit: per_base_bed + tuple val(meta), path('*.per-base.bed.gz.csi') , optional:true, emit: per_base_csi + tuple val(meta), path('*.regions.bed.gz') , optional:true, emit: regions_bed + tuple val(meta), path('*.regions.bed.gz.csi') , optional:true, emit: regions_csi + tuple val(meta), path('*.quantized.bed.gz') , optional:true, emit: quantized_bed + tuple val(meta), path('*.quantized.bed.gz.csi') , optional:true, emit: quantized_csi + tuple val(meta), path('*.thresholds.bed.gz') , optional:true, emit: thresholds_bed + tuple val(meta), path('*.thresholds.bed.gz.csi'), optional:true, emit: thresholds_csi + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def reference = fasta ? "--fasta ${fasta}" : "" + def interval = bed ? "--by ${bed}" : "" + if (bed && args.contains("--by")) { + error "'--by' can only be specified once when running mosdepth! Either remove input BED file definition or remove '--by' from 'ext.args' definition" + } + if (!bed && args.contains("--thresholds")) { + error "'--thresholds' can only be specified in conjunction with '--by'" + } + + """ + mosdepth \\ + --threads $task.cpus \\ + $interval \\ + $reference \\ + $args \\ + $prefix \\ + $bam + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + mosdepth: \$(mosdepth --version 2>&1 | sed 's/^.*mosdepth //; s/ .*\$//') + END_VERSIONS + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.global.dist.txt + touch ${prefix}.region.dist.txt + touch ${prefix}.summary.txt + touch ${prefix}.per-base.d4 + touch ${prefix}.per-base.bed.gz + touch ${prefix}.per-base.bed.gz.csi + touch ${prefix}.regions.bed.gz + touch ${prefix}.regions.bed.gz.csi + touch ${prefix}.quantized.bed.gz + touch ${prefix}.quantized.bed.gz.csi + touch ${prefix}.thresholds.bed.gz + touch ${prefix}.thresholds.bed.gz.csi + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + mosdepth: \$(mosdepth --version 2>&1 | sed 's/^.*mosdepth //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/mosdepth/meta.yml b/modules/nf-core/mosdepth/meta.yml new file mode 100644 index 00000000..adf3893f --- /dev/null +++ b/modules/nf-core/mosdepth/meta.yml @@ -0,0 +1,109 @@ +name: mosdepth +description: Calculates genome-wide sequencing coverage. +keywords: + - mosdepth + - bam + - cram + - coverage +tools: + - mosdepth: + description: | + Fast BAM/CRAM depth calculation for WGS, exome, or targeted sequencing. + documentation: https://github.com/brentp/mosdepth + doi: 10.1093/bioinformatics/btx699 + licence: ["MIT"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - bam: + type: file + description: Input BAM/CRAM file + pattern: "*.{bam,cram}" + - bai: + type: file + description: Index for BAM/CRAM file + pattern: "*.{bai,crai}" + - meta2: + type: map + description: | + Groovy Map containing bed information + e.g. [ id:'test' ] + - bed: + type: file + description: BED file with intersected intervals + pattern: "*.{bed}" + - meta3: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'test' ] + - fasta: + type: file + description: Reference genome FASTA file + pattern: "*.{fa,fasta}" +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - global_txt: + type: file + description: Text file with global cumulative coverage distribution + pattern: "*.{global.dist.txt}" + - regions_txt: + type: file + description: Text file with region cumulative coverage distribution + pattern: "*.{region.dist.txt}" + - summary_txt: + type: file + description: Text file with summary mean depths per chromosome and regions + pattern: "*.{summary.txt}" + - per_base_bed: + type: file + description: BED file with per-base coverage + pattern: "*.{per-base.bed.gz}" + - per_base_csi: + type: file + description: Index file for BED file with per-base coverage + pattern: "*.{per-base.bed.gz.csi}" + - per_base_d4: + type: file + description: D4 file with per-base coverage + pattern: "*.{per-base.d4}" + - regions_bed: + type: file + description: BED file with per-region coverage + pattern: "*.{regions.bed.gz}" + - regions_csi: + type: file + description: Index file for BED file with per-region coverage + pattern: "*.{regions.bed.gz.csi}" + - quantized_bed: + type: file + description: BED file with binned coverage + pattern: "*.{quantized.bed.gz}" + - quantized_csi: + type: file + description: Index file for BED file with binned coverage + pattern: "*.{quantized.bed.gz.csi}" + - thresholds_bed: + type: file + description: BED file with the number of bases in each region that are covered at or above each threshold + pattern: "*.{thresholds.bed.gz}" + - thresholds_csi: + type: file + description: Index file for BED file with threshold coverage + pattern: "*.{thresholds.bed.gz.csi}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@joseespinosa" + - "@drpatelh" + - "@ramprasadn" + - "@matthdsm" diff --git a/modules/nf-core/qualimap/rnaseq/main.nf b/modules/nf-core/qualimap/rnaseq/main.nf deleted file mode 100644 index 044c983f..00000000 --- a/modules/nf-core/qualimap/rnaseq/main.nf +++ /dev/null @@ -1,63 +0,0 @@ -process QUALIMAP_RNASEQ { - tag "$meta.id" - label 'process_medium' - - conda "bioconda::qualimap=2.2.2d" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/qualimap:2.2.2d--1' : - 'biocontainers/qualimap:2.2.2d--1' }" - - input: - tuple val(meta), path(bam) - tuple val(meta2), path(gtf) - - output: - tuple val(meta), path("${prefix}"), emit: results - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - prefix = task.ext.prefix ?: "${meta.id}" - def paired_end = meta.single_end ? '' : '-pe' - def memory = (task.memory.mega*0.8).intValue() + 'M' - - def strandedness = 'non-strand-specific' - if (meta.strandedness == 'forward') { - strandedness = 'strand-specific-forward' - } else if (meta.strandedness == 'reverse') { - strandedness = 'strand-specific-reverse' - } - """ - unset DISPLAY - mkdir -p tmp - export _JAVA_OPTIONS=-Djava.io.tmpdir=./tmp - qualimap \\ - --java-mem-size=$memory \\ - rnaseq \\ - $args \\ - -bam $bam \\ - -gtf $gtf \\ - -p $strandedness \\ - $paired_end \\ - -outdir $prefix - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - qualimap: \$(echo \$(qualimap 2>&1) | sed 's/^.*QualiMap v.//; s/Built.*\$//') - END_VERSIONS - """ - - stub: - prefix = task.ext.prefix ?: "${meta.id}" - """ - mkdir ${prefix} - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - qualimap: \$(echo \$(qualimap 2>&1) | sed 's/^.*QualiMap v.//; s/Built.*\$//') - END_VERSIONS - """ -} diff --git a/modules/nf-core/qualimap/rnaseq/meta.yml b/modules/nf-core/qualimap/rnaseq/meta.yml deleted file mode 100644 index 7738f08d..00000000 --- a/modules/nf-core/qualimap/rnaseq/meta.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: qualimap_rnaseq -description: Evaluate alignment data -keywords: - - quality control - - qc - - rnaseq -tools: - - qualimap: - description: | - Qualimap 2 is a platform-independent application written in - Java and R that provides both a Graphical User Interface and - a command-line interface to facilitate the quality control of - alignment sequencing data and its derivatives like feature counts. - homepage: http://qualimap.bioinfo.cipf.es/ - documentation: http://qualimap.conesalab.org/doc_html/index.html - doi: 10.1093/bioinformatics/bts503 - licence: ["GPL-2.0-only"] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM file - pattern: "*.{bam}" - - meta2: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'test' ] - - gtf: - type: file - description: GTF file of the reference genome - pattern: "*.{gtf}" -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - results: - type: directory - description: Qualimap results dir - pattern: "*/*" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@FriederikeHanssen" diff --git a/subworkflows/local/qc_workflow.nf b/subworkflows/local/qc_workflow.nf index bdf887d1..204d78ca 100644 --- a/subworkflows/local/qc_workflow.nf +++ b/subworkflows/local/qc_workflow.nf @@ -2,7 +2,7 @@ // Check input samplesheet and get read channels // -include { QUALIMAP_RNASEQ } from '../../modules/nf-core/qualimap/rnaseq/main' +include { MOSDEPTH } from '../../modules/nf-core/mosdepth/main' include { PICARD_COLLECTRNASEQMETRICS } from '../../modules/local/picard/collectrnaseqmetrics/main' include { PICARD_MARKDUPLICATES } from '../../modules/nf-core/picard/markduplicates/main' @@ -19,9 +19,15 @@ workflow QC_WORKFLOW { main: ch_versions = Channel.empty() - QUALIMAP_RNASEQ(ch_bam_sorted, ch_chrgtf) - ch_versions = ch_versions.mix(QUALIMAP_RNASEQ.out.versions) - ch_qualimap_qc = Channel.empty().mix(QUALIMAP_RNASEQ.out.results) + ch_bam_sorted_indexed + .map { meta, bam, bai -> + return [meta, bam, bai, []] + }.set { ch_bam_sorted_indexed_bed } + + MOSDEPTH(ch_bam_sorted_indexed_bed, ch_fasta) + ch_versions = ch_versions.mix(MOSDEPTH.out.versions) + ch_mosdepth_summary = Channel.empty().mix(MOSDEPTH.out.summary_txt) + ch_mosdepth_global = Channel.empty().mix(MOSDEPTH.out.global_txt) PICARD_COLLECTRNASEQMETRICS(ch_bam_sorted_indexed, ch_refflat, ch_rrna_interval) ch_versions = ch_versions.mix(PICARD_COLLECTRNASEQMETRICS.out.versions) @@ -34,7 +40,8 @@ workflow QC_WORKFLOW { emit: versions = ch_versions.ifEmpty(null) - qualimap_qc = ch_qualimap_qc + mosdepth_summary = ch_mosdepth_summary + mosdepth_gobal = ch_mosdepth_global rnaseq_metrics = ch_rnaseq_metrics duplicate_metrics = ch_duplicate_metrics diff --git a/tower.yml b/tower.yml index 5813f5d3..b578f6c5 100644 --- a/tower.yml +++ b/tower.yml @@ -15,6 +15,8 @@ reports: display: "Fusion-report HTML report" "**/megafusion/*_fusion_data.vcf": display: "Collected statistics on each fusion fed to FusionInspector in VCF format" + "**/mosdepth/*.mosdepth.summary.txt": + display: "All samples summary of mean depths per chromosome and within specified regions per chromosome" "**/picard/*.MarkDuplicates.metrics.txt": display: "Picard: Metrics from CollectRnaMetrics" "**/picard/*_rna_metrics.txt": diff --git a/workflows/rnafusion.nf b/workflows/rnafusion.nf index 6f498e11..266bc3cd 100644 --- a/workflows/rnafusion.nf +++ b/workflows/rnafusion.nf @@ -284,7 +284,8 @@ workflow RNAFUSION { ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) - ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.qualimap_qc.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.mosdepth_summary.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.mosdepth_gobal.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(STARFUSION_WORKFLOW.out.star_stats.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.rnaseq_metrics.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.duplicate_metrics.collect{it[1]}.ifEmpty([])) From 992636f642af8e6ee4b3cfac9fa15f4c4b5c4112 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:37:48 +0200 Subject: [PATCH 010/175] remove squid/pizzly first steps --- .github/workflows/awsfulltest.yml | 94 -------------------- .github/workflows/awstest.yml | 98 --------------------- .github/workflows/ci.yml | 28 ------ CITATIONS.md | 11 --- README.md | 28 ++---- bin/megafusion.py | 10 +-- containers/arriba/Dockerfile | 14 --- containers/arriba/environment.yml | 14 --- containers/ericscript/Dockerfile | 17 ---- containers/ericscript/environment.yml | 8 -- containers/fusioncatcher/Dockerfile | 49 ----------- containers/pizzly/Dockerfile | 14 --- containers/pizzly/environment.yml | 9 -- containers/squid/Dockerfile | 19 ---- containers/squid/environment.yml | 11 --- docs/output.md | 83 +---------------- docs/usage.md | 8 +- modules.json | 5 -- modules/local/fusionreport/detect/main.nf | 4 +- modules/local/fusionreport/detect/meta.yml | 8 -- modules/local/kallisto/quant/main.nf | 47 ---------- modules/local/kallisto/quant/meta.yml | 44 --------- modules/local/pizzly/detect/main.nf | 49 ----------- modules/local/pizzly/detect/meta.yml | 44 --------- modules/local/pizzly/download/main.nf | 40 --------- modules/local/squid/annotate/main.nf | 41 --------- modules/local/squid/annotate/meta.yml | 36 -------- modules/local/squid/detect/main.nf | 40 --------- modules/local/squid/detect/meta.yml | 41 --------- modules/nf-core/kallisto/index/main.nf | 44 --------- modules/nf-core/kallisto/index/meta.yml | 43 --------- nextflow.config | 6 -- nextflow_schema.json | 32 +------ subworkflows/local/fusionreport_workflow.nf | 4 - subworkflows/local/pizzly_workflow.nf | 37 -------- subworkflows/local/squid_workflow.nf | 80 ----------------- tower.yml | 4 - workflows/build_references.nf | 7 +- workflows/rnafusion.nf | 25 ------ 39 files changed, 21 insertions(+), 1175 deletions(-) delete mode 100644 containers/arriba/Dockerfile delete mode 100644 containers/arriba/environment.yml delete mode 100644 containers/ericscript/Dockerfile delete mode 100644 containers/ericscript/environment.yml delete mode 100644 containers/fusioncatcher/Dockerfile delete mode 100644 containers/pizzly/Dockerfile delete mode 100644 containers/pizzly/environment.yml delete mode 100644 containers/squid/Dockerfile delete mode 100644 containers/squid/environment.yml delete mode 100644 modules/local/kallisto/quant/main.nf delete mode 100644 modules/local/kallisto/quant/meta.yml delete mode 100644 modules/local/pizzly/detect/main.nf delete mode 100644 modules/local/pizzly/detect/meta.yml delete mode 100644 modules/local/pizzly/download/main.nf delete mode 100644 modules/local/squid/annotate/main.nf delete mode 100644 modules/local/squid/annotate/meta.yml delete mode 100644 modules/local/squid/detect/main.nf delete mode 100644 modules/local/squid/detect/meta.yml delete mode 100644 modules/nf-core/kallisto/index/main.nf delete mode 100644 modules/nf-core/kallisto/index/meta.yml delete mode 100644 subworkflows/local/pizzly_workflow.nf delete mode 100644 subworkflows/local/squid_workflow.nf diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 31c6cd5f..f25848e7 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -62,53 +62,6 @@ jobs: tower_action_*.log tower_action_*.json - - name: Launch build squid workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "squid": true, - "build_references": true - } - profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch squid workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "squid": true, - } - profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - name: Launch build starfusion workflow via tower uses: seqeralabs/action-tower-launch@v2 with: @@ -203,53 +156,6 @@ jobs: tower_action_*.log tower_action_*.json - - name: Launch build pizzly workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "pizzly": true, - "build_references": true - } - profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch pizzly workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "pizzly": true, - } - profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - name: Launch stringtie workflow via tower uses: seqeralabs/action-tower-launch@v2 with: diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml index 003f6786..d43fe90b 100644 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -61,55 +61,6 @@ jobs: tower_action_*.log tower_action_*.json - - name: Launch build squid workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "squid": true, - "stub": true, - "build_references": true - } - profiles: test,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch squid workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "squid": true, - "stub": true - } - profiles: test,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - name: Launch build starfusion workflow via tower uses: seqeralabs/action-tower-launch@v2 with: @@ -208,55 +159,6 @@ jobs: tower_action_*.log tower_action_*.json - - name: Launch build pizzly workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "pizzly": true, - "build_references": true, - "stub": true - } - profiles: test,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch pizzly workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "pizzly": true, - "stub": true - } - profiles: test,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - name: Launch stringtie workflow via tower uses: seqeralabs/action-tower-launch@v2 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e62dcf41..d31089a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,34 +49,6 @@ jobs: --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - name: Dry test squid build - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub --build_references \ - --outdir /home/runner/work/rnafusion/rnafusion/results --squid \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - - name: Dry test squid - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub \ - --outdir /home/runner/work/rnafusion/rnafusion/results --squid \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - - name: Dry test pizzly build - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub --build_references \ - --outdir /home/runner/work/rnafusion/rnafusion/results --pizzly \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - - name: Dry test pizzly - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub \ - --outdir /home/runner/work/rnafusion/rnafusion/results --pizzly \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - name: Dry test fusioncatcher build run: | nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub --build_references \ diff --git a/CITATIONS.md b/CITATIONS.md index 42e5e50c..2b666093 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -42,10 +42,6 @@ > Van der Auwera GA. Somatic variation discovery with GATK4. Proceedings of the American Association for Cancer Research Annual Meeting 2017. 2017 Apr 1-5. Cancer Res 2017;77(13 Suppl) doi:10.1158/1538-7445.AM2017-3590 -- [Kallisto](https://pachterlab.github.io/kallisto/) - - > Bray NL, Pimentel H, Melsted P, Pachter L. Near-optimal probabilistic RNA-seq quantification. Nature Biotechnology 2016 Apr. 34, 525–527. doi:10.1038/nbt.3519. PMID: 27043002. - - [MegaFusion](https://github.com/J35P312/MegaFusion) - [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) @@ -54,9 +50,6 @@ - [picard-tools](http://broadinstitute.github.io/picard) -- [Pizzly](https://github.com/pmelsted/pizzly) - Melsted P, Hateley S, Joseph IC, Pimentel H, Bray N, Pachter L. Fusion detection and quantification by pseudoalignment. BioRxiv, 2017 Jul. doi: 10.1101/166322. - - [Qualimap 2](https://pubmed.ncbi.nlm.nih.gov/26428292/) > Okonechnikov K, Conesa A, García-Alcalde F. Qualimap 2: advanced multi-sample quality control for high-throughput sequencing data Bioinformatics. 2016 Jan 15;32(2):292-4. doi: 10.1093/bioinformatics/btv566. Epub 2015 Oct 1. PubMed PMID: 26428292; PubMed Central PMCID: PMC4708105. @@ -65,10 +58,6 @@ > 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. -- [Squid](https://github.com/Kingsford-Group/squid) - - > Ma C, Shao M, Kingsford C. SQUID: transcriptomic structural variation detection from RNA-seq. Genome Biol 2028 Apr. 19, 52. doi: 10.1186/s13059-018-1421-5. PubMed PMID: 29650026. PubMed Central PMCID: PMC5896115. - - [STAR](https://pubmed.ncbi.nlm.nih.gov/23104886/) > Dobin A, Davis CA, Schlesinger F, Drenkow J, Zaleski C, Jha S, Batut P, Chaisson M, Gingeras TR. STAR: ultrafast universal RNA-seq aligner Bioinformatics. 2013 Jan 1;29(1):15-21. doi: 10.1093/bioinformatics/bts635. Epub 2012 Oct 25. PubMed PMID: 23104886; PubMed Central PMCID: PMC3530905. diff --git a/README.md b/README.md index a6f3e750..b54f6e2c 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,8 @@ In rnafusion the full-sized test includes reference building and fusion detectio 2. Create [STAR](https://github.com/alexdobin/STAR) index 3. Download [Arriba](https://github.com/suhrig/arriba) references 4. Download [FusionCatcher](https://github.com/ndaniel/fusioncatcher) references -5. Download [Pizzly](https://github.com/pmelsted/pizzly) references ([kallisto](https://pachterlab.github.io/kallisto/manual) index) -6. Download and build [STAR-Fusion](https://github.com/STAR-Fusion/STAR-Fusion) references -7. Download [Fusion-report](https://github.com/Clinical-Genomics/fusion-report) DBs +5. Download and build [STAR-Fusion](https://github.com/STAR-Fusion/STAR-Fusion) references +6. Download [Fusion-report](https://github.com/Clinical-Genomics/fusion-report) DBs #### Main workflow @@ -43,31 +42,22 @@ In rnafusion the full-sized test includes reference building and fusion detectio 5. Arriba subworkflow - [STAR](https://github.com/alexdobin/STAR) alignment - [Arriba](https://github.com/suhrig/arriba) fusion detection -6. Pizzly subworkflow - - [Kallisto](https://pachterlab.github.io/kallisto/) quantification - - [Pizzly](https://github.com/pmelsted/pizzly) fusion detection -7. Squid subworkflow - - [STAR](https://github.com/alexdobin/STAR) alignment - - [Samtools view](http://www.htslib.org/): convert sam output from STAR to bam - - [Samtools sort](http://www.htslib.org/): bam output from STAR - - [SQUID](https://github.com/Kingsford-Group/squid) fusion detection - - [SQUID](https://github.com/Kingsford-Group/squid) annotate -8. STAR-fusion subworkflow +6. STAR-fusion subworkflow - [STAR](https://github.com/alexdobin/STAR) alignment - [STAR-Fusion](https://github.com/STAR-Fusion/STAR-Fusion) fusion detection -9. Fusioncatcher subworkflow +7. Fusioncatcher subworkflow - [FusionCatcher](https://github.com/ndaniel/fusioncatcher) fusion detection -10. StringTie subworkflow +8. StringTie subworkflow - [StringTie](https://ccb.jhu.edu/software/stringtie/) -11. Fusion-report +9. Fusion-report - Merge all fusions detected by the selected tools with [Fusion-report](https://github.com/Clinical-Genomics/fusion-report) -12. Post-processing and analysis of data +10. Post-processing and analysis of data - [FusionInspector](https://github.com/FusionInspector/FusionInspector) - [Arriba](https://github.com/suhrig/arriba) visualisation - QC for mapped reads ([`QualiMap: BAM QC`](https://kokonech.github.io/qualimap/HG00096.chr20_bamqc/qualimapReport.html)) - Collect metrics ([`picard CollectRnaSeqMetrics`](https://gatk.broadinstitute.org/hc/en-us/articles/360037057492-CollectRnaSeqMetrics-Picard-) and ([`picard MarkDuplicates`](https://gatk.broadinstitute.org/hc/en-us/articles/360037052812-MarkDuplicates-Picard-)) -13. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) -14. Compress bam files to cram with [samtools view](http://www.htslib.org/) +11. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) +12. Compress bam files to cram with [samtools view](http://www.htslib.org/) ## Usage diff --git a/bin/megafusion.py b/bin/megafusion.py index 76872b57..76a6a180 100755 --- a/bin/megafusion.py +++ b/bin/megafusion.py @@ -65,8 +65,6 @@ def header_def(sample): ##INFO=\n\ ##INFO=\n\ ##INFO=\n\ -##INFO=\n\ -##INFO=\n\ ##INFO=\n\ ##INFO=\n\ ##INFO=\n\ @@ -105,10 +103,6 @@ def read_build_fusionreport(fusionreport_file): fusion_report["arriba"] = "" if not "fusioncatcher" in fusion_report.columns: fusion_report["fusioncatcher"] = "" - if not "pizzly" in fusion_report.columns: - fusion_report["pizzly"] = "" - if not "squid" in fusion_report.columns: - fusion_report["squid"] = "" if not "starfusion" in fusion_report.columns: fusion_report["starfusion"] = "" return fusion_report @@ -140,7 +134,7 @@ def column_manipulation(df): # INFO df.loc[index, "INFO"] = ( "SVTYPE=BND;CHRA={};CHRB={};GENEA={};GENEB={};ORIENTATION={},{};FOUND_DB={};" - "ARRIBA={};FUSIONCATCHER={};PIZZLY={};SQUID={};STARFUSION={};TOOL_HITS={};SCORE={}".format( + "ARRIBA={};FUSIONCATCHER={};STARFUSION={};TOOL_HITS={};SCORE={}".format( row["chromosomeA"], row["chromosomeB"], row["geneA"], @@ -150,8 +144,6 @@ def column_manipulation(df): row["found_db"], row["arriba"], row["fusioncatcher"], - row["pizzly"], - row["squid"], row["starfusion"], row["tools_hits"], row["score"], diff --git a/containers/arriba/Dockerfile b/containers/arriba/Dockerfile deleted file mode 100644 index ea36d90f..00000000 --- a/containers/arriba/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM nfcore/base:1.9 - -LABEL authors="Martin Proks" \ - description="Docker image containing all requirements for nfcore/rnafusion pipeline" - -# Install the conda environment -COPY environment.yml / -RUN conda env create -f /environment.yml && conda clean -a - -# Add conda installation dir to PATH (instead of doing 'conda activate') -ENV PATH /opt/conda/envs/nf-core-rnafusion-arriba_1.2.0/bin:$PATH - -# Dump the details of the installed packages to a file for posterity -RUN conda env export --name nf-core-rnafusion-arriba_1.2.0 > nf-core-rnafusion-arriba_1.2.0.yml diff --git a/containers/arriba/environment.yml b/containers/arriba/environment.yml deleted file mode 100644 index 20a1024e..00000000 --- a/containers/arriba/environment.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: nf-core-rnafusion-arriba_1.2.0 -channels: - - conda-forge - - bioconda - - defaults -dependencies: - - bioconda::arriba=1.2.0 - - bioconda::bioconductor-genomicalignments - - bioconda::bioconductor-genomicranges - - bioconda::samtools=1.9 - - bioconda::star=2.7.1a - - conda-forge::openssl=1.0 - - conda-forge::r-circlize - - conda-forge::readline=6.2 diff --git a/containers/ericscript/Dockerfile b/containers/ericscript/Dockerfile deleted file mode 100644 index 70b87c48..00000000 --- a/containers/ericscript/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM nfcore/base:1.9 - -LABEL authors="Martin Proks" \ - description="Docker image containing all requirements for nfcore/rnafusion pipeline" - -# Install the conda environment -COPY environment.yml / -RUN conda env create -f /environment.yml && conda clean -a - -# Add conda installation dir to PATH (instead of doing 'conda activate') -ENV PATH /opt/conda/envs/nf-core-rnafusion-ericscript_0.5.5/bin:$PATH - -# Ignore database check (https://github.com/nf-core/rnafusion/issues/119) -RUN echo 1 > /opt/conda/envs/nf-core-rnafusion-ericscript_0.5.5/share/ericscript-0.5.5-4/lib/data/_resources/.flag.dbexists - -# Dump the details of the installed packages to a file for posterity -RUN conda env export --name nf-core-rnafusion-ericscript_0.5.5 > nf-core-rnafusion-ericscript_0.5.5.yml diff --git a/containers/ericscript/environment.yml b/containers/ericscript/environment.yml deleted file mode 100644 index 1e76273c..00000000 --- a/containers/ericscript/environment.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: nf-core-rnafusion-ericscript_0.5.5 -channels: - - conda-forge - - bioconda - - defaults -dependencies: - - bioconda::ericscript=0.5.5 - - conda-forge::ncurses=6.1 diff --git a/containers/fusioncatcher/Dockerfile b/containers/fusioncatcher/Dockerfile deleted file mode 100644 index 77efaf57..00000000 --- a/containers/fusioncatcher/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -FROM ubuntu:18.04 - -LABEL Description="This image is used to run FusionCatcher" Version="1.33" - -RUN apt-get -y clean \ - && apt-get -y update \ - && apt-get -y install \ - automake \ - build-essential \ - bzip2 \ - cmake \ - curl \ - g++ \ - gawk \ - gcc \ - gzip \ - libc6-dev \ - libncurses5-dev \ - libtbb2 \ - libtbb-dev \ - make \ - parallel \ - pigz \ - python \ - python-dev \ - python-biopython \ - python-numpy \ - python-openpyxl \ - python-xlrd \ - tar \ - unzip \ - wget \ - zip \ - zlib1g \ - zlib1g-dev \ - zlibc \ - default-jdk \ - && apt-get -y clean - -WORKDIR /opt - -###################### -## INSTALLATION -###################### - -RUN wget --no-check-certificate http://sf.net/projects/fusioncatcher/files/bootstrap.py -O bootstrap.py \ - && python bootstrap.py -t -y -i /opt/fusioncatcher/v1.33/ - -ENV PATH /opt/fusioncatcher/v1.33/bin:$PATH diff --git a/containers/pizzly/Dockerfile b/containers/pizzly/Dockerfile deleted file mode 100644 index 0056bf9b..00000000 --- a/containers/pizzly/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM nfcore/base:1.9 - -LABEL authors="Martin Proks" \ - description="Docker image containing all requirements for nfcore/rnafusion pipeline" - -# Install the conda environment -COPY environment.yml / -RUN conda env create -f /environment.yml && conda clean -a - -# Add conda installation dir to PATH (instead of doing 'conda activate') -ENV PATH /opt/conda/envs/nf-core-rnafusion-pizzly_0.37.3/bin:$PATH - -# Dump the details of the installed packages to a file for posterity -RUN conda env export --name nf-core-rnafusion-pizzly_0.37.3 > nf-core-rnafusion-pizzly_0.37.3.yml diff --git a/containers/pizzly/environment.yml b/containers/pizzly/environment.yml deleted file mode 100644 index 79974871..00000000 --- a/containers/pizzly/environment.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: nf-core-rnafusion-pizzly_0.37.3 -channels: - - conda-forge - - bioconda - - defaults -dependencies: - - bioconda::kallisto=0.44.0 - - bioconda::pizzly=0.37.3 - - conda-forge::pigz=2.3.4 diff --git a/containers/squid/Dockerfile b/containers/squid/Dockerfile deleted file mode 100644 index 20d390ee..00000000 --- a/containers/squid/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM nfcore/base:1.9 - -LABEL authors="Martin Proks" \ - description="Docker image containing all requirements for nfcore/rnafusion pipeline" - -# Install the conda environment -COPY environment.yml / -RUN conda env create -f /environment.yml && conda clean -a - -# Add conda installation dir to PATH (instead of doing 'conda activate') -ENV PATH /opt/conda/envs/nf-core-rnafusion-squid_1.5-star2.7.1a/bin:$PATH - -RUN cd /opt/conda/envs/nf-core-rnafusion-squid_1.5-star2.7.1a/bin \ - && wget https://raw.githubusercontent.com/Kingsford-Group/squid/f45c9025d41cffd982ecbbdd52844e5a4f074de9/utils/AnnotateSQUIDOutput.py \ - && chmod +x /opt/conda/envs/nf-core-rnafusion-squid_1.5-star2.7.1a/bin/AnnotateSQUIDOutput.py \ - && ln -s /opt/conda/envs/nf-core-rnafusion-squid_1.5-star2.7.1a/bin/python3 /bin/python - -# Dump the details of the installed packages to a file for posterity -RUN conda env export --name nf-core-rnafusion-squid_1.5-star2.7.1a > nf-core-rnafusion-squid_1.5-star2.7.1a.yml diff --git a/containers/squid/environment.yml b/containers/squid/environment.yml deleted file mode 100644 index 7385b163..00000000 --- a/containers/squid/environment.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: nf-core-rnafusion-squid_1.5-star2.7.1a -channels: - - conda-forge - - bioconda - - defaults -dependencies: - - bioconda::samtools=1.9 - - bioconda::squid=1.5 - - bioconda::star=2.7.1a - - conda-forge::numpy=1.18.1 - - conda-forge::python=3.7.6 diff --git a/docs/output.md b/docs/output.md index 4144891b..f24d0db7 100644 --- a/docs/output.md +++ b/docs/output.md @@ -11,11 +11,9 @@ The directories listed below will be created in the results directory after the The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using the following steps: - [Download and build references](#references) - Build references needed to run the rest of the pipeline -- [STAR](#star) - Alignment for arriba, squid and STAR-fusion +- [STAR](#star) - Alignment for arriba, and STAR-fusion - [Cat](#cat) - Concatenate fastq files per sample ID - [Arriba](#arriba) - Arriba fusion detection -- [Pizzly](#pizzly) - Pizzly fusion detection -- [Squid](#squid) - Squid fusion detection - [STAR-fusion](#starfusion) - STAR-fusion fusion detection - [StringTie](#stringtie) - StringTie assembly - [FusionCatcher](#fusioncatcher) - Fusion catcher fusion detection @@ -55,8 +53,6 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `fusiongdb.db` - `fusiongdb2.db` - `mitelman.db` - - `pizzly` - - `kallisto` - file containing the kallisto index - `star` - dir with STAR index - `starfusion` - files and dirs used to build the index @@ -78,7 +74,6 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d ├── arriba_visualisation ├── cram_arriba ├── cram_starfusion -├── cram_squid ├── fastp ├── fastqc ├── fusioncatcher @@ -88,15 +83,11 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d ├── megafusion ├── multiqc ├── picard -├── pizzly ├── pipeline_info -├── pizzly ├── qualimap ├── samtools_sort_for_arriba -├── squid ├── star_for_arriba ├── star_for_starfusion -├── star_for_squid ├── starfusion └── work .nextflow.log @@ -281,26 +272,6 @@ Picard CollectRnaMetrics and picard MarkDuplicates share the same output directo -#### Pizzly - -Pizzly uses the following arguments: - -```bash --k 31 \ ---align-score 2 \ ---insert-size 400 \ ---cache index.cache.txt -``` - -
-Output files - -- `pizzly` - - `.pizzly.txt` - contains the identified fusions - - `.pizzly.unfiltered.json` - -
- ### Qualimap
@@ -317,26 +288,14 @@ Pizzly uses the following arguments: ### Samtools -#### Samtools view - -Samtools view is used to convert the chimeric SAM output from STAR_FOR_SQUID to BAM - -
-Output files - -- `samtools_view_for_squid` - - `_chimeric.bam` - sorted BAM file - -
- #### Samtools sort -Samtools sort is used to sort BAM files from STAR_FOR_STARFUSION (for arriba visualisation) and the chimeric BAM from STAR_FOR_SQUID +Samtools sort is used to sort BAM files from STAR_FOR_STARFUSION (for arriba visualisation)
Output files -- `samtools_sort_for_` +- `samtools_sort_for_` - `(_chimeric)_sorted.bam` - sorted BAM file
@@ -353,19 +312,6 @@ Samtools index is used to index BAM files from STAR_FOR_ARRIBA (for arriba visua
-### Squid - -Squid is run in two steps: i) fusion detection and ii) fusion annotation, but the output is in a shared `squid` directory - -
-Output files - -- `squid` - - `.squid.fusions_sv.txt` - contains the identified fusions - - `.squid.fusions.annotated.txt`- contains the identified fusions annotated - -
- ### STAR STAR is used to align to genome reference @@ -393,20 +339,6 @@ For `arriba` with the parameters: --chimMultimapNmax 50 ``` -For `squid` with the parameters: - -```bash ---twopassMode Basic \ ---chimOutType SeparateSAMold \ ---chimSegmentMin 20 \ ---chimJunctionOverhangMin 12 \ ---alignSJDBoverhangMin 10 \ ---outReadsUnmapped Fastx \ ---outSAMstrandField intronMotif \ ---outSAMtype BAM SortedByCoordinate \ ---readFilesCommand zcat -``` - For `STAR-fusion` with the parameters: ```bash @@ -435,7 +367,7 @@ For `STAR-fusion` with the parameters: --quantMode GeneCounts ``` -> STAR_FOR_STARFUSION uses `${params.ensembl}/Homo_sapiens.GRCh38.${params.ensembl_version}.chr.gtf` whereas STAR_FOR_ARRIBA and STAR_FOR_SQUID use `${params.ensembl_ref}/Homo_sapiens.GRCh38.${params.ensembl_version}.gtf` +> STAR_FOR_STARFUSION uses `${params.ensembl}/Homo_sapiens.GRCh38.${params.ensembl_version}.chr.gtf` whereas STAR_FOR_ARRIBA uses `${params.ensembl_ref}/Homo_sapiens.GRCh38.${params.ensembl_version}.gtf`
Output files @@ -451,13 +383,6 @@ For `STAR-fusion` with the parameters: - `.Aligned.out.bam` -**For squid:** - -- `.Aligned.sortedByCoord.out.bam` -- `.Chimeric.out.sam` -- `.unmapped_1.fastq.gz` -- `.unmapped_2.fastq.gz` - **For starfusion:** - `.Aligned.sortedByCoord.out.bam` diff --git a/docs/usage.md b/docs/usage.md index 9df6d24e..8b4fa8fd 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -13,7 +13,7 @@ The pipeline is divided into two parts: - required only once before running the pipeline - **Important**: has to be run with each new release 2. Detecting fusions - - Supported tools: `Arriba`, `FusionCatcher`, `pizzly`, `SQUID`, `STAR-Fusion`, and `StringTie` + - Supported tools: `Arriba`, `FusionCatcher`, `STAR-Fusion`, and `StringTie` - QC: `Fastqc`, `MultiQC`, and `Qualimap rnaseq` - Fusions visualization: `Arriba`, `fusion-report` and `FusionInspector`, VCF file creation based on `MegaFusion` @@ -123,7 +123,7 @@ As you can see above for multiple runs of the same sample, the `sample` name has ### Starting commands -The pipeline can either be run using all fusion detection tools or specifying individual tools. Visualisation tools will be run on all fusions detected. To run all tools (`arriba`, `fusioncatcher`, `pizzly`, `squid`, `starfusion`, `stringtie`) use the `--all` parameter: +The pipeline can either be run using all fusion detection tools or specifying individual tools. Visualisation tools will be run on all fusions detected. To run all tools (`arriba`, `fusioncatcher`, `starfusion`, `stringtie`) use the `--all` parameter: ```bash nextflow run nf-core/rnafusion \ @@ -299,9 +299,9 @@ There are two parameters to increase the `--limitSjdbInsertNsj` parameter if nec - `--fusioncatcher_limitSjdbInsertNsj`, default: 2000000 - `--fusioninspector_limitSjdbInsertNsj`, default: 1000000 -Use the parameter `--cram` to compress the BAM files to CRAM for specific tools. Options: arriba, squid, starfusion. Leave no space between options: +Use the parameter `--cram` to compress the BAM files to CRAM for specific tools. Options: arriba, starfusion. Leave no space between options: -- `--cram arriba,squid,starfusion`, default: [] +- `--cram arriba,starfusion`, default: [] - `--cram arriba` ### Updating the pipeline diff --git a/modules.json b/modules.json index 162a71b5..1e81bc7e 100644 --- a/modules.json +++ b/modules.json @@ -45,11 +45,6 @@ "git_sha": "541811d779026c5d395925895fa5ed35e7216cc0", "installed_by": ["modules"] }, - "kallisto/index": { - "branch": "master", - "git_sha": "699fa6f3002d922380615f3847198aeb57d8b6a9", - "installed_by": ["modules"] - }, "multiqc": { "branch": "master", "git_sha": "a6e11ac655e744f7ebc724be669dd568ffdc0e80", diff --git a/modules/local/fusionreport/detect/main.nf b/modules/local/fusionreport/detect/main.nf index 38809803..0d674a7a 100644 --- a/modules/local/fusionreport/detect/main.nf +++ b/modules/local/fusionreport/detect/main.nf @@ -8,7 +8,7 @@ process FUSIONREPORT { input: - tuple val(meta), path(reads), path(arriba_fusions), path(pizzly_fusions), path(squid_fusions), path(starfusion_fusions), path(fusioncatcher_fusions) + tuple val(meta), path(reads), path(arriba_fusions), path(starfusion_fusions), path(fusioncatcher_fusions) tuple val(meta2), path(fusionreport_ref) output: @@ -27,8 +27,6 @@ process FUSIONREPORT { def args = task.ext.args ?: '' def args2 = task.ext.args2 ?: '' def tools = params.arriba || params.all ? "--arriba ${arriba_fusions} " : '' - tools += params.pizzly || params.all ? "--pizzly ${pizzly_fusions} " : '' - tools += params.squid || params.all ? "--squid ${squid_fusions} " : '' tools += params.starfusion || params.all ? "--starfusion ${starfusion_fusions} " : '' tools += params.fusioncatcher || params.all ? "--fusioncatcher ${fusioncatcher_fusions} " : '' def prefix = task.ext.prefix ?: "${meta.id}" diff --git a/modules/local/fusionreport/detect/meta.yml b/modules/local/fusionreport/detect/meta.yml index 7b9de84c..83ba76c7 100644 --- a/modules/local/fusionreport/detect/meta.yml +++ b/modules/local/fusionreport/detect/meta.yml @@ -24,14 +24,6 @@ input: type: path description: File pattern: "*.fusions.tsv" - - pizzly_fusions: - type: path - description: File containing fusions from pizzly - pattern: "*.pizzly.txt" - - squid_fusions: - type: path - description: File containing fusions from squid - pattern: "*.annotated.txt" - starfusion_fusions: type: path description: File containing fusions from STARfusion diff --git a/modules/local/kallisto/quant/main.nf b/modules/local/kallisto/quant/main.nf deleted file mode 100644 index 7d3e5dfb..00000000 --- a/modules/local/kallisto/quant/main.nf +++ /dev/null @@ -1,47 +0,0 @@ -process KALLISTO_QUANT { - tag "$meta.id" - label 'process_medium' - - conda "bioconda::kallisto=0.46.2" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/kallisto:0.46.2--h4f7b962_1' : - 'quay.io/biocontainers/kallisto:0.46.2--h4f7b962_1' }" - - - input: - tuple val(meta), path(reads) - path index - - output: - path "versions.yml" , emit: versions - tuple val(meta), path("*fusions.txt") , emit: txt - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - """ - kallisto quant \ - -t $task.cpus \ - -i $index \ - --fusion \ - -o . \ - $reads - mv fusion.txt ${prefix}.kallisto_quant.fusions.txt - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - kallisto: \$(echo \$(kallisto 2>&1) | sed 's/^kallisto //; s/Usage.*\$//') - END_VERSIONS - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - """ - touch ${prefix}.kallisto_quant.fusions.txt - cat <<-END_VERSIONS > versions.yml - "${task.process}": - kallisto: \$(echo \$(kallisto 2>&1) | sed 's/^kallisto //; s/Usage.*\$//') - END_VERSIONS - """ -} - diff --git a/modules/local/kallisto/quant/meta.yml b/modules/local/kallisto/quant/meta.yml deleted file mode 100644 index 31821aa6..00000000 --- a/modules/local/kallisto/quant/meta.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: kallisto_quant -description: runs the kallisto quantification algorithm - - quant -tools: - - kallisto: - description: Quantifying abundances of transcripts from bulk and single-cell RNA-Seq data, or more generally of target sequences using high-throughput sequencing reads. - homepage: https://pachterlab.github.io/kallisto/ - documentation: https://pachterlab.github.io/kallisto/manual - tool_dev_url: https://github.com/pachterlab/kallisto - doi: "" - licence: ["BSD-2-Clause"] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: FASTQ file - pattern: "*.{fastq}" - - reference: - type: directory - description: Path to kallisto index - pattern: "*" - -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - - fusions: - type: file - description: fusions - pattern: "*.txt" - -authors: - - "@rannick" diff --git a/modules/local/pizzly/detect/main.nf b/modules/local/pizzly/detect/main.nf deleted file mode 100644 index a610b531..00000000 --- a/modules/local/pizzly/detect/main.nf +++ /dev/null @@ -1,49 +0,0 @@ -process PIZZLY { - tag "$meta.id" - label 'process_medium' - - conda "bioconda::kallisto=0.46.2 bioconda::pizzly==0.37.3" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pizzly:0.37.3--py36_2' : - 'quay.io/biocontainers/pizzly:0.37.3--h470a237_3' }" - - input: - tuple val(meta), path(txt) - tuple val(meta2), path(transcript) - tuple val(meta3), path(gtf) - - output: - path "versions.yml" , emit: versions - tuple val(meta), path("*pizzly.txt") , emit: fusions - tuple val(meta), path("*unfiltered.json") , emit: fusions_unfiltered - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - """ - pizzly \\ - $args \\ - --gtf $gtf \\ - --fasta $transcript \\ - --output ${prefix}.pizzly $txt - - pizzly_flatten_json.py ${prefix}.pizzly.json ${prefix}.pizzly.txt - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - pizzly: \$(pizzly --version | grep pizzly | sed -e "s/pizzly version: //g") - END_VERSIONS - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - """ - touch ${prefix}.pizzly.txt - touch ${prefix}.pizzly.unfiltered.json - cat <<-END_VERSIONS > versions.yml - "${task.process}": - pizzly: \$(pizzly --version | grep pizzly | sed -e "s/pizzly version: //g") - END_VERSIONS - """ -} - diff --git a/modules/local/pizzly/detect/meta.yml b/modules/local/pizzly/detect/meta.yml deleted file mode 100644 index 930b3a62..00000000 --- a/modules/local/pizzly/detect/meta.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: pizzly -description: Pizzly detection of fusions. -keywords: - - fusion - - pizzly -tools: - - pizzly: - description: Fast fusion detection using kallisto - homepage: https://github.com/pmelsted/pizzly - documentation: https://github.com/pmelsted/pizzly - tool_dev_url: https://github.com/pmelsted/pizzly - doi: "" - licence: ["BSD-2-Clause"] - -input: - - fasta: - type: file - description: genome fasta file - pattern: "*.{fasta*}" - - reference: - type: directory - description: Path to kallisto index - pattern: "*" - - gtf: - type: file - description: gtf reference - pattern: "*.gtf" - -output: - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - - fusions: - type: file - description: fusions - pattern: "*pizzly.txt" - - unfiltered: - type: file - description: unfiltered fusions - pattern: "*unfiltered.json" - -authors: - - "@rannick" diff --git a/modules/local/pizzly/download/main.nf b/modules/local/pizzly/download/main.nf deleted file mode 100644 index efaae3aa..00000000 --- a/modules/local/pizzly/download/main.nf +++ /dev/null @@ -1,40 +0,0 @@ -process PIZZLY_DOWNLOAD { - tag "pizzly" - label 'process_medium' - - conda "bioconda::kallisto=0.46.2" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/kallisto:0.46.2--h4f7b962_1' : - 'quay.io/biocontainers/kallisto:0.46.2--h4f7b962_1' }" - - input: - tuple val(meta), path(transcript) - - output: - path "versions.yml" , emit: versions - path "index.idx" , emit: reference - - script: - def args = task.ext.args ?: '' - """ - kallisto index \\ - -i index.idx \\ - $args \\ - $transcript - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - kallisto: \$(echo \$(kallisto 2>&1) | sed 's/^kallisto //; s/Usage.*\$//') - END_VERSIONS """ - - stub: - """ - touch index.idx - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - kallisto: \$(echo \$(kallisto 2>&1) | sed 's/^kallisto //; s/Usage.*\$//') - END_VERSIONS - """ - -} diff --git a/modules/local/squid/annotate/main.nf b/modules/local/squid/annotate/main.nf deleted file mode 100644 index 9b6eebe7..00000000 --- a/modules/local/squid/annotate/main.nf +++ /dev/null @@ -1,41 +0,0 @@ - -process SQUID_ANNOTATE { - tag "$meta.id" - label 'process_medium' - - conda "bioconda::squid=1.5" - container "docker.io/nfcore/rnafusion:squid_1.5-star2.7.1a" - - - - input: - tuple val(meta), path(txt) - tuple val(meta2), path(gtf) - - output: - tuple val(meta), path("*annotated.txt") , emit: fusions_annotated - path "versions.yml" , emit: versions - - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - """ - AnnotateSQUIDOutput.py $gtf $txt ${prefix}.squid.fusions.annotated.txt - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - squid: \$(echo \$(squid --version 2>&1) | sed 's/v//') - END_VERSIONS - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - """ - touch ${prefix}.squid.fusions.annotated.txt - cat <<-END_VERSIONS > versions.yml - "${task.process}": - squid: \$(echo \$(squid --version 2>&1) | sed 's/v//') - END_VERSIONS - """ -} diff --git a/modules/local/squid/annotate/meta.yml b/modules/local/squid/annotate/meta.yml deleted file mode 100644 index e1a1f0d2..00000000 --- a/modules/local/squid/annotate/meta.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: squid -description: Squid detection of fusions. -keywords: - - fusion - - pizzly -tools: - - pizzly: - description: Fusion detection using squid - homepage: https://github.com/Kingsford-Group/squid - documentation: https://github.com/Kingsford-Group/squid - tool_dev_url: https://github.com/Kingsford-Group/squid - doi: "" - licence: ["BSD-3-Clause"] - -input: - - fusions: - type: directory - description: Path to squid fusions - pattern: "*.txt" - - gtf: - type: file - description: gtf reference - pattern: "*.gtf" - -output: - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - - fusions_annotated: - type: file - description: squid fusions annotated - pattern: "*squid.fusions.annotated.txt" - -authors: - - "@rannick" diff --git a/modules/local/squid/detect/main.nf b/modules/local/squid/detect/main.nf deleted file mode 100644 index 3ccb6e3e..00000000 --- a/modules/local/squid/detect/main.nf +++ /dev/null @@ -1,40 +0,0 @@ - -process SQUID { - tag "squid" - label 'process_medium' - - conda "bioconda::squid=1.5" - container "docker.io/nfcore/rnafusion:squid_1.5-star2.7.1a" - - - - input: - tuple val(meta), path(bam), path(chimeric_bam) - - output: - tuple val(meta), path("*sv.txt") , emit: fusions - path "versions.yml" , emit: versions - - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - """ - squid -b $bam -c $chimeric_bam -o ${prefix}.squid.fusions - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - squid: \$(echo \$(squid --version 2>&1) | sed 's/v//') - END_VERSIONS - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - """ - touch ${prefix}.squid.fusions_sv.txt - cat <<-END_VERSIONS > versions.yml - "${task.process}": - squid: \$(echo \$(squid --version 2>&1) | sed 's/v//') - END_VERSIONS - """ -} diff --git a/modules/local/squid/detect/meta.yml b/modules/local/squid/detect/meta.yml deleted file mode 100644 index a7f1e61a..00000000 --- a/modules/local/squid/detect/meta.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: squid -description: Squid detection of fusions. -keywords: - - fusion - - pizzly -tools: - - pizzly: - description: Fusion detection using squid - homepage: https://github.com/Kingsford-Group/squid - documentation: https://github.com/Kingsford-Group/squid - tool_dev_url: https://github.com/Kingsford-Group/squid - doi: "" - licence: ["BSD-3-Clause"] - -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}" - - chimeric_bam: - type: file - description: BAM/CRAM/SAM file containing only chimeric sorted reads - pattern: "*.{bam,cram,sam}" - -output: - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - - fusions: - type: directory - description: Path to squid fusions - pattern: "*.txt" - -authors: - - "@rannick" diff --git a/modules/nf-core/kallisto/index/main.nf b/modules/nf-core/kallisto/index/main.nf deleted file mode 100644 index fb9e44d9..00000000 --- a/modules/nf-core/kallisto/index/main.nf +++ /dev/null @@ -1,44 +0,0 @@ -process KALLISTO_INDEX { - tag "$fasta" - label 'process_medium' - - conda "bioconda::kallisto=0.46.2" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/kallisto:0.46.2--h4f7b962_1' : - 'biocontainers/kallisto:0.46.2--h4f7b962_1' }" - - input: - tuple val(meta), path(fasta) - - output: - tuple val(meta), path("kallisto") , emit: index - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - """ - kallisto \\ - index \\ - $args \\ - -i kallisto \\ - $fasta - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - kallisto: \$(echo \$(kallisto 2>&1) | sed 's/^kallisto //; s/Usage.*\$//') - END_VERSIONS - """ - - stub: - """ - touch kallisto - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - kallisto: \$(echo \$(kallisto 2>&1) | sed 's/^kallisto //; s/Usage.*\$//') - END_VERSIONS - """ -} diff --git a/modules/nf-core/kallisto/index/meta.yml b/modules/nf-core/kallisto/index/meta.yml deleted file mode 100644 index 05dfa53d..00000000 --- a/modules/nf-core/kallisto/index/meta.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: kallisto_index -description: Create kallisto index -keywords: - - kallisto - - kallisto/index - - index -tools: - - kallisto: - description: Quantifying abundances of transcripts from bulk and single-cell RNA-Seq data, or more generally of target sequences using high-throughput sequencing reads. - homepage: https://pachterlab.github.io/kallisto/ - documentation: https://pachterlab.github.io/kallisto/manual - tool_dev_url: https://github.com/pachterlab/kallisto - - licence: ["BSD-2-Clause"] - -input: - - meta: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'test' ] - - fasta: - type: file - description: genome fasta file - pattern: "*.{fasta}" - -output: - - meta: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'test' ] - - index: - type: directory - description: Kallisto genome index - pattern: "*.idx" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - -authors: - - "@ggabernet" diff --git a/nextflow.config b/nextflow.config index 994e96e3..4f8330f8 100644 --- a/nextflow.config +++ b/nextflow.config @@ -55,8 +55,6 @@ params { all = false arriba = false fusioncatcher = false - pizzly = false - squid = false starindex = false starfusion = false stringtie = false @@ -75,8 +73,6 @@ params { arriba_ref_known_fusions = "${params.genomes_base}/arriba/known_fusions_hg38_GRCh38_v2.3.0.tsv.gz" arriba_ref_protein_domains = "${params.genomes_base}/arriba/protein_domains_hg38_GRCh38_v2.3.0.gff3" fusioncatcher_ref = "${params.genomes_base}/fusioncatcher/human_v102" - pizzly_ref = "${params.genomes_base}/pizzly/kallisto" - squid_ref = "${params.genomes_base}/squid" starfusion_ref = "${params.genomes_base}/starfusion/ctat_genome_lib_build_dir" starindex_ref = "${params.genomes_base}/star" fusionreport_ref = "${params.genomes_base}/fusion_report_db" @@ -84,8 +80,6 @@ params { // Path to fusion outputs arriba_fusions = null - pizzly_fusions = null - squid_fusions = null starfusion_fusions = null fusioncatcher_fusions = null fusioninspector_fusions = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 02cd1e27..b8ed5d97 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -185,36 +185,6 @@ "default": true, "description": "Display fusions identified with 2 tools or more" }, - "pizzly": { - "type": "boolean", - "fa_icon": "far fa-file-code", - "description": "Build or run pizzly references/analyses" - }, - "pizzly_fusions": { - "type": "string", - "fa_icon": "far fa-file-code", - "description": "Path to pizzly output" - }, - "pizzly_ref": { - "type": "string", - "fa_icon": "far fa-file-code", - "description": "Path to pizzly references" - }, - "squid": { - "type": "boolean", - "fa_icon": "far fa-file-code", - "description": "Build or run squid references/analyses" - }, - "squid_fusions": { - "type": "string", - "fa_icon": "far fa-file-code", - "description": "Path to squid output" - }, - "squid_ref": { - "type": "string", - "fa_icon": "far fa-file-code", - "description": "Path to squid references" - }, "starfusion": { "type": "boolean", "fa_icon": "far fa-file-code", @@ -303,7 +273,7 @@ "properties": { "cram": { "type": "string", - "description": "List of tools for which to compress BAM file to CRAM,default: [], options: arriba, squid, starfusion. Leave no space between options", + "description": "List of tools for which to compress BAM file to CRAM,default: [], options: arriba, starfusion. Leave no space between options", "fa_icon": "fas fa-cut" } } diff --git a/subworkflows/local/fusionreport_workflow.nf b/subworkflows/local/fusionreport_workflow.nf index 478986a4..25238e1b 100644 --- a/subworkflows/local/fusionreport_workflow.nf +++ b/subworkflows/local/fusionreport_workflow.nf @@ -6,8 +6,6 @@ workflow FUSIONREPORT_WORKFLOW { reads fusionreport_ref arriba_fusions - pizzly_fusions - squid_fusions starfusion_fusions fusioncatcher_fusions @@ -18,8 +16,6 @@ workflow FUSIONREPORT_WORKFLOW { if (!params.fusioninspector_only) { reads_fusions = reads .join(arriba_fusions, remainder: true) - .join(pizzly_fusions, remainder: true) - .join(squid_fusions, remainder: true) .join(starfusion_fusions, remainder: true) .join(fusioncatcher_fusions, remainder: true) diff --git a/subworkflows/local/pizzly_workflow.nf b/subworkflows/local/pizzly_workflow.nf deleted file mode 100644 index 7675432b..00000000 --- a/subworkflows/local/pizzly_workflow.nf +++ /dev/null @@ -1,37 +0,0 @@ -include { KALLISTO_QUANT } from '../../modules/local/kallisto/quant/main' -include { PIZZLY } from '../../modules/local/pizzly/detect/main' - -workflow PIZZLY_WORKFLOW { - take: - reads - ch_gtf - ch_transcript - - main: - ch_versions = Channel.empty() - ch_dummy_file = file("$baseDir/assets/dummy_file_pizzly.txt", checkIfExists: true) - - if ((params.pizzly || params.all) && !params.fusioninspector_only) { - if (params.pizzly_fusions) { - ch_pizzly_fusions = reads.combine(Channel.value(file(params.pizzly_fusions, checkIfExists:true))) - .map { meta, reads, fusions -> [ meta, fusions ] } - } else { - KALLISTO_QUANT(reads, params.pizzly_ref ) - ch_versions = ch_versions.mix(KALLISTO_QUANT.out.versions) - - PIZZLY( KALLISTO_QUANT.out.txt, ch_transcript, ch_gtf ) - ch_versions = ch_versions.mix(PIZZLY.out.versions) - - ch_pizzly_fusions = PIZZLY.out.fusions - } - } - else { - ch_pizzly_fusions = reads.combine(Channel.value(file(ch_dummy_file, checkIfExists:true))) - .map { meta, reads, fusions -> [ meta, fusions ] } - } - - emit: - fusions = ch_pizzly_fusions - versions = ch_versions.ifEmpty(null) - } - diff --git a/subworkflows/local/squid_workflow.nf b/subworkflows/local/squid_workflow.nf deleted file mode 100644 index c4f29425..00000000 --- a/subworkflows/local/squid_workflow.nf +++ /dev/null @@ -1,80 +0,0 @@ -include { SAMTOOLS_INDEX as SAMTOOLS_INDEX_FOR_SQUID } from '../../modules/nf-core/samtools/index/main' -include { SAMTOOLS_INDEX as SAMTOOLS_INDEX_FOR_SQUID_CHIMERIC } from '../../modules/nf-core/samtools/index/main' -include { SAMTOOLS_SORT as SAMTOOLS_SORT_FOR_SQUID_CHIMERIC } from '../../modules/nf-core/samtools/sort/main' -include { SAMTOOLS_VIEW as SAMTOOLS_VIEW_FOR_SQUID_CHIMERIC } from '../../modules/nf-core/samtools/view/main' -include { SAMTOOLS_VIEW as SAMTOOLS_VIEW_FOR_SQUID_CRAM } from '../../modules/nf-core/samtools/view/main' -include { SAMTOOLS_VIEW as SAMTOOLS_VIEW_FOR_SQUID_CRAM_CHIMERIC } from '../../modules/nf-core/samtools/view/main' -include { SQUID } from '../../modules/local/squid/detect/main' -include { SQUID_ANNOTATE } from '../../modules/local/squid/annotate/main' -include { STAR_ALIGN as STAR_FOR_SQUID } from '../../modules/nf-core/star/align/main' - -workflow SQUID_WORKFLOW { - - take: - reads - ch_gtf - ch_starindex_ensembl_ref - ch_fasta - - main: - ch_versions = Channel.empty() - ch_dummy_file = file("$baseDir/assets/dummy_file_squid.txt", checkIfExists: true) - - if ((params.squid || params.all) && !params.fusioninspector_only) { - if (params.squid_fusions){ - ch_squid_fusions = reads.combine(Channel.value(file(params.squid_fusions, checkIfExists:true))) - .map { meta, reads, fusions -> [ meta, fusions ] } - } else { - - STAR_FOR_SQUID(reads, ch_starindex_ensembl_ref, ch_gtf, params.star_ignore_sjdbgtf, '', params.seq_center ?: '') - ch_versions = ch_versions.mix(STAR_FOR_SQUID.out.versions) - - STAR_FOR_SQUID.out.sam - .map { meta, sam -> - return [meta, sam, []] - }.set { chimeric_sam } - - - - SAMTOOLS_VIEW_FOR_SQUID_CHIMERIC (chimeric_sam, ch_fasta, []) - ch_versions = ch_versions.mix(SAMTOOLS_VIEW_FOR_SQUID_CHIMERIC.out.versions) - - SAMTOOLS_SORT_FOR_SQUID_CHIMERIC (SAMTOOLS_VIEW_FOR_SQUID_CHIMERIC.out.bam) - ch_versions = ch_versions.mix(SAMTOOLS_SORT_FOR_SQUID_CHIMERIC.out.versions) - - bam_chimeric = STAR_FOR_SQUID.out.bam_sorted.join(SAMTOOLS_SORT_FOR_SQUID_CHIMERIC.out.bam) - - if (params.cram.contains('squid')){ - SAMTOOLS_INDEX_FOR_SQUID(STAR_FOR_SQUID.out.bam_sorted) - ch_versions = ch_versions.mix(SAMTOOLS_INDEX_FOR_SQUID.out.versions) - SAMTOOLS_INDEX_FOR_SQUID_CHIMERIC(SAMTOOLS_SORT_FOR_SQUID_CHIMERIC.out.bam) - ch_versions = ch_versions.mix(SAMTOOLS_INDEX_FOR_SQUID_CHIMERIC.out.versions) - - bam_sorted_indexed = STAR_FOR_SQUID.out.bam_sorted.join(SAMTOOLS_INDEX_FOR_SQUID.out.bai) - chimeric_sorted_indexed = SAMTOOLS_SORT_FOR_SQUID_CHIMERIC.out.bam.join(SAMTOOLS_INDEX_FOR_SQUID_CHIMERIC.out.bai) - - SAMTOOLS_VIEW_FOR_SQUID_CRAM (bam_sorted_indexed, ch_fasta, []) - ch_versions = ch_versions.mix(SAMTOOLS_VIEW_FOR_SQUID_CRAM.out.versions) - SAMTOOLS_VIEW_FOR_SQUID_CRAM_CHIMERIC (chimeric_sorted_indexed, ch_fasta, []) - ch_versions = ch_versions.mix(SAMTOOLS_VIEW_FOR_SQUID_CRAM.out.versions) - } - - SQUID (bam_chimeric) - ch_versions = ch_versions.mix(SQUID.out.versions) - - SQUID_ANNOTATE (SQUID.out.fusions, ch_gtf) - ch_versions = ch_versions.mix(SQUID_ANNOTATE.out.versions) - - ch_squid_fusions = SQUID_ANNOTATE.out.fusions_annotated - } - } - else { - ch_squid_fusions = reads.combine(Channel.value(file(ch_dummy_file, checkIfExists:true))) - .map { meta, reads, fusions -> [ meta, fusions ] } - } - - emit: - fusions = ch_squid_fusions - versions = ch_versions.ifEmpty(null) - } - diff --git a/tower.yml b/tower.yml index 5813f5d3..1aa32e24 100644 --- a/tower.yml +++ b/tower.yml @@ -19,14 +19,10 @@ reports: display: "Picard: Metrics from CollectRnaMetrics" "**/picard/*_rna_metrics.txt": display: "Picard: Metrics from MarkDuplicates" - "**/pizzly/*.pizzly.txt": - display: "Pizzly identified fusion TXT report" "**/qualimap/qualimapReport.html": display: "Qualimap HTML report from STAR_FOR_STARFUSION alignment" "**/qualimap/rnaseq_qc_results.txt": display: "Qualimap QC results from STAR_FOR_STARFUSION alignment in TXT format" - "**/squid/*.squid.fusions.annotated.txt": - display: "Squid identified fusion TXT report" "**/star_for_starfusion/*ReadsPerGene.out.tab": display: "Number of reads per gene" "**/starfusion/*.starfusion.fusion_predictions.tsv": diff --git a/workflows/build_references.nf b/workflows/build_references.nf index 6a03edb6..4921443a 100644 --- a/workflows/build_references.nf +++ b/workflows/build_references.nf @@ -21,7 +21,6 @@ include { CONVERT2BED } from '../modules/local/convert2bed/m include { SAMTOOLS_FAIDX } from '../modules/nf-core/samtools/faidx/main' include { STAR_GENOMEGENERATE } from '../modules/nf-core/star/genomegenerate/main' -include { KALLISTO_INDEX as PIZZLY_INDEX } from '../modules/nf-core/kallisto/index/main' include { GATK4_CREATESEQUENCEDICTIONARY } from '../modules/nf-core/gatk4/createsequencedictionary/main' include { GATK4_BEDTOINTERVALLIST } from '../modules/nf-core/gatk4/bedtointervallist/main' @@ -47,7 +46,7 @@ workflow BUILD_REFERENCES { GATK4_BEDTOINTERVALLIST(CONVERT2BED.out.bed, GATK4_CREATESEQUENCEDICTIONARY.out.dict) - if (params.starindex || params.all || params.starfusion || params.arriba || params.squid ) { + if (params.starindex || params.all || params.starfusion || params.arriba) { STAR_GENOMEGENERATE( ENSEMBL_DOWNLOAD.out.fasta, ENSEMBL_DOWNLOAD.out.gtf ) } @@ -59,10 +58,6 @@ workflow BUILD_REFERENCES { FUSIONCATCHER_DOWNLOAD() } - if (params.pizzly || params.all) { - PIZZLY_INDEX( ENSEMBL_DOWNLOAD.out.transcript ) - } - if (params.starfusion || params.all) { if (params.starfusion_build){ STARFUSION_BUILD( ENSEMBL_DOWNLOAD.out.fasta, ENSEMBL_DOWNLOAD.out.chrgtf ) diff --git a/workflows/rnafusion.nf b/workflows/rnafusion.nf index 6f498e11..8783570b 100644 --- a/workflows/rnafusion.nf +++ b/workflows/rnafusion.nf @@ -54,7 +54,6 @@ if (params_fasta_path_uri){ else { for (param in checkPathParamList) if ((param.toString())!= file(param).toString() && !params.build_references) { exit 1, "Problem with ${param}: ABSOLUTE PATHS are required! Check for trailing '/' at the end of paths too." } } -if ((params.squid || params.all) && params.ensembl_version != 102) { exit 1, 'Ensembl version is not supported by squid' } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -80,9 +79,7 @@ ch_multiqc_custom_methods_description = params.multiqc_methods_description ? fil include { INPUT_CHECK } from '../subworkflows/local/input_check' include { TRIM_WORKFLOW } from '../subworkflows/local/trim_workflow' include { ARRIBA_WORKFLOW } from '../subworkflows/local/arriba_workflow' -include { PIZZLY_WORKFLOW } from '../subworkflows/local/pizzly_workflow' include { QC_WORKFLOW } from '../subworkflows/local/qc_workflow' -include { SQUID_WORKFLOW } from '../subworkflows/local/squid_workflow' include { STARFUSION_WORKFLOW } from '../subworkflows/local/starfusion_workflow' include { STRINGTIE_WORKFLOW } from '../subworkflows/local/stringtie_workflow' include { FUSIONCATCHER_WORKFLOW } from '../subworkflows/local/fusioncatcher_workflow' @@ -180,26 +177,6 @@ workflow RNAFUSION { ) ch_versions = ch_versions.mix(ARRIBA_WORKFLOW.out.versions.first().ifEmpty(null)) - // Run pizzly/kallisto - - PIZZLY_WORKFLOW ( - ch_reads_all, - ch_gtf, - ch_transcript - ) - ch_versions = ch_versions.mix(PIZZLY_WORKFLOW.out.versions.first().ifEmpty(null)) - - -// Run squid - - SQUID_WORKFLOW ( - ch_reads_all, - ch_gtf, - ch_starindex_ensembl_ref, - ch_fasta - ) - ch_versions = ch_versions.mix(SQUID_WORKFLOW.out.versions.first().ifEmpty(null)) - //Run STAR fusion STARFUSION_WORKFLOW ( @@ -231,8 +208,6 @@ workflow RNAFUSION { ch_reads_all, ch_fusionreport_ref, ARRIBA_WORKFLOW.out.fusions, - PIZZLY_WORKFLOW.out.fusions, - SQUID_WORKFLOW.out.fusions, STARFUSION_WORKFLOW.out.fusions, FUSIONCATCHER_WORKFLOW.out.fusions ) From 0535880964c4ab0c1e555e1d177cde57db5b7866 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:39:32 +0200 Subject: [PATCH 011/175] bump version to 3.0.0 --- assets/multiqc_config.yml | 4 ++-- nextflow.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 5e0f10e0..44a9684d 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,7 +1,7 @@ report_comment: > - This report has been generated by the nf-core/rnafusion + This report has been generated by the nf-core/rnafusion analysis pipeline. For information about how to interpret these results, please see the - documentation. + documentation. report_section_order: "nf-core-rnafusion-methods-description": order: -1000 diff --git a/nextflow.config b/nextflow.config index 994e96e3..909edafe 100644 --- a/nextflow.config +++ b/nextflow.config @@ -295,7 +295,7 @@ manifest { description = """Nextflow rnafusion analysis pipeline, part of the nf-core community.""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '2.4.0' + version = '3.0.0dev' doi = '' } From 889df95f80ec8993e17d28cb5070ea92206d253e Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:25:00 +0200 Subject: [PATCH 012/175] update modules.config --- conf/modules.config | 73 ++------------------------------------------- 1 file changed, 3 insertions(+), 70 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index b4dc96d6..51d41ce2 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -133,14 +133,6 @@ process { ] } - withName: KALLISTO_INDEX { - ext.args = '-k 31' - publishDir = [ - path: { "${params.genomes_base}/pizzly" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - ] - } withName: MEGAFUSION { ext.when = {!params.fusioninspector_only} ext.prefix = { "${meta.id}_fusion_data" } @@ -157,20 +149,16 @@ process { } - withName: PICARD_MARKDUPLICATES { + withName: GATK4_MARKDUPLICATES { ext.when = { !params.skip_qc && !params.fusioninspector_only && (params.starfusion || params.all) } - } - - withName: PIZZLY { - ext.args = "-k 31 --align-score 2 --insert-size 400 --cache index.cache.txt" publishDir = [ - path: { "${params.outdir}/pizzly" }, + path: { "${params.outdir}/picard" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, ] } - withName: QUALIMAP_RNASEQ { + withName: MOSDEPTH { ext.when = { !params.skip_qc && !params.fusioninspector_only && (params.starfusion || params.all)} } @@ -203,15 +191,6 @@ process { ] } - withName: SAMTOOLS_SORT_FOR_SQUID_CHIMERIC { - ext.prefix = { "${meta.id}_chimeric_sorted" } - publishDir = [ - path: { "${params.outdir}/samtools_sort_for_squid_chimeric" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - withName: SAMTOOLS_VIEW_FOR_ARRIBA { ext.args = { "--output-fmt cram" } ext.prefix = { "${meta.id}_star_for_arriba" } @@ -222,35 +201,6 @@ process { ] } - withName: SAMTOOLS_VIEW_FOR_SQUID_CHIMERIC { - ext.prefix = { "${meta.id}_chimeric" } - ext.args = { "--output-fmt bam" } - publishDir = [ - path: { "${params.outdir}/samtools_view_for_squid_chimeric" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: SAMTOOLS_VIEW_FOR_SQUID_CRAM { - ext.args = { "--output-fmt cram" } - publishDir = [ - path: { "${params.outdir}/cram_squid" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: SAMTOOLS_VIEW_FOR_SQUID_CRAM_CHIMERIC { - ext.args = { "--output-fmt cram" } - ext.prefix = { "${meta.id}_chimeric" } - publishDir = [ - path: { "${params.outdir}/cram_squid" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - withName: SAMTOOLS_VIEW_FOR_STARFUSION { ext.args = { "--output-fmt cram" } ext.prefix = { "${meta.id}.star_for_starfusion.Aligned.sortedByCoord.out" } @@ -285,23 +235,6 @@ process { --chimMultimapNmax 50' } - withName: STAR_FOR_SQUID { - publishDir = [ - path: { "${params.outdir}/star_for_squid" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - ] - ext.args = '--twopassMode Basic \ - --chimOutType SeparateSAMold \ - --chimSegmentMin 20 \ - --chimJunctionOverhangMin 12 \ - --alignSJDBoverhangMin 10 \ - --outReadsUnmapped Fastx \ - --outSAMstrandField intronMotif \ - --outSAMtype BAM SortedByCoordinate \ - --readFilesCommand zcat' - } - withName: STAR_FOR_STARFUSION { publishDir = [ path: { "${params.outdir}/star_for_starfusion" }, From f7fe23ad6306ed955e7144b0911e690491d96902 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:27:16 +0200 Subject: [PATCH 013/175] update changelog --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9ff036..17be2667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ 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). +## v3.0.0dev + +### Added + +### Changed + +- Removed `--fusioninspector_filter` and `--fusionreport_filter` in favor of `--tools_cutoff` (default = 1, no filters applied) [#389](https://github.com/nf-core/rnafusion/pull/389) + +### Fixed + +### Removed + ## v2.4.0 - [2023/09/22] ### Added From 80b634718eb55585a4c51336e9168b5f9661bffd Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:39:59 +0200 Subject: [PATCH 014/175] prettify --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b54f6e2c..c933176d 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,9 @@ In rnafusion the full-sized test includes reference building and fusion detectio 7. Fusioncatcher subworkflow - [FusionCatcher](https://github.com/ndaniel/fusioncatcher) fusion detection 8. StringTie subworkflow - - [StringTie](https://ccb.jhu.edu/software/stringtie/) + - [StringTie](https://ccb.jhu.edu/software/stringtie/) 9. Fusion-report - - Merge all fusions detected by the selected tools with [Fusion-report](https://github.com/Clinical-Genomics/fusion-report) + - Merge all fusions detected by the selected tools with [Fusion-report](https://github.com/Clinical-Genomics/fusion-report) 10. Post-processing and analysis of data - [FusionInspector](https://github.com/FusionInspector/FusionInspector) - [Arriba](https://github.com/suhrig/arriba) visualisation From 318733163ee60b96deab16194d973c597f0dd310 Mon Sep 17 00:00:00 2001 From: nf-core-bot Date: Tue, 26 Sep 2023 08:09:03 +0000 Subject: [PATCH 015/175] Template update for nf-core/tools version 2.10 --- .devcontainer/devcontainer.json | 1 + .github/CONTRIBUTING.md | 4 +- .github/workflows/linting.yml | 2 +- .github/workflows/release-announcments.yml | 68 +++++++++ CHANGELOG.md | 2 +- CITATIONS.md | 2 +- CODE_OF_CONDUCT.md | 133 ++++++++++++++---- README.md | 21 +-- assets/multiqc_config.yml | 4 +- conf/modules.config | 9 ++ docs/output.md | 5 +- docs/usage.md | 16 ++- lib/NfcoreTemplate.groovy | 16 +++ lib/WorkflowRnafusion.groovy | 2 +- main.nf | 3 + modules.json | 6 +- .../custom/dumpsoftwareversions/main.nf | 2 +- modules/nf-core/fastqc/main.nf | 8 +- modules/nf-core/multiqc/main.nf | 2 +- nextflow.config | 9 +- nextflow_schema.json | 15 -- workflows/rnafusion.nf | 1 + 22 files changed, 253 insertions(+), 78 deletions(-) create mode 100644 .github/workflows/release-announcments.yml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ea27a584..4ecfbfe3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,6 +2,7 @@ "name": "nfcore", "image": "nfcore/gitpod:latest", "remoteUser": "gitpod", + "runArgs": ["--privileged"], // Configure tool-specific properties. "customizations": { diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 975df6cd..5e32dfd8 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -9,7 +9,9 @@ Please use the pre-filled template to save time. However, don't be put off by this template - other more general issues and suggestions are welcome! Contributions to the code are even more welcome ;) -> If you need help using or modifying nf-core/rnafusion then the best place to ask is on the nf-core Slack [#rnafusion](https://nfcore.slack.com/channels/rnafusion) channel ([join our Slack here](https://nf-co.re/join/slack)). +:::info +If you need help using or modifying nf-core/rnafusion then the best place to ask is on the nf-core Slack [#rnafusion](https://nfcore.slack.com/channels/rnafusion) channel ([join our Slack here](https://nf-co.re/join/slack)). +::: ## Contribution workflow diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 888cb4bc..b8bdd214 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -78,7 +78,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.11" architecture: "x64" - name: Install dependencies diff --git a/.github/workflows/release-announcments.yml b/.github/workflows/release-announcments.yml new file mode 100644 index 00000000..6ad33927 --- /dev/null +++ b/.github/workflows/release-announcments.yml @@ -0,0 +1,68 @@ +name: release-announcements +# Automatic release toot and tweet anouncements +on: + release: + types: [published] + workflow_dispatch: + +jobs: + toot: + runs-on: ubuntu-latest + steps: + - uses: rzr/fediverse-action@master + with: + access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} + host: "mstdn.science" # custom host if not "mastodon.social" (default) + # GitHub event payload + # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release + message: | + Pipeline release! ${{ github.repository }} v${{ github.event.release.tag_name }} - ${{ github.event.release.name }}! + + Please see the changelog: ${{ github.event.release.html_url }} + + send-tweet: + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install dependencies + run: pip install tweepy==4.14.0 + - name: Send tweet + shell: python + run: | + import os + import tweepy + + client = tweepy.Client( + access_token=os.getenv("TWITTER_ACCESS_TOKEN"), + access_token_secret=os.getenv("TWITTER_ACCESS_TOKEN_SECRET"), + consumer_key=os.getenv("TWITTER_CONSUMER_KEY"), + consumer_secret=os.getenv("TWITTER_CONSUMER_SECRET"), + ) + tweet = os.getenv("TWEET") + client.create_tweet(text=tweet) + env: + TWEET: | + Pipeline release! ${{ github.repository }} v${{ github.event.release.tag_name }} - ${{ github.event.release.name }}! + + Please see the changelog: ${{ github.event.release.html_url }} + TWITTER_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_KEY }} + TWITTER_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_SECRET }} + TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }} + TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} + + bsky-post: + runs-on: ubuntu-latest + steps: + - uses: zentered/bluesky-post-action@v0.0.2 + with: + post: | + Pipeline release! ${{ github.repository }} v${{ github.event.release.tag_name }} - ${{ github.event.release.name }}! + + Please see the changelog: ${{ github.event.release.html_url }} + env: + BSKY_IDENTIFIER: ${{ secrets.BSKY_IDENTIFIER }} + BSKY_PASSWORD: ${{ secrets.BSKY_PASSWORD }} + # diff --git a/CHANGELOG.md b/CHANGELOG.md index 5470be26..d09f1be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## v2.3.4 - [date] +## v3.0.0dev - [date] Initial release of nf-core/rnafusion, created with the [nf-core](https://nf-co.re/) template. diff --git a/CITATIONS.md b/CITATIONS.md index 1d65b743..98bdbc60 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -12,7 +12,7 @@ - [FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/) - > Andrews, S. (2010). FastQC: A Quality Control Tool for High Throughput Sequence Data [Online]. Available online https://www.bioinformatics.babraham.ac.uk/projects/fastqc/. + > Andrews, S. (2010). FastQC: A Quality Control Tool for High Throughput Sequence Data [Online]. - [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f4fd052f..c089ec78 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,18 +1,20 @@ -# Code of Conduct at nf-core (v1.0) +# Code of Conduct at nf-core (v1.4) ## Our Pledge -In the interest of fostering an open, collaborative, and welcoming environment, we as contributors and maintainers of nf-core, pledge to making participation in our projects and community a harassment-free experience for everyone, regardless of: +In the interest of fostering an open, collaborative, and welcoming environment, we as contributors and maintainers of nf-core pledge to making participation in our projects and community a harassment-free experience for everyone, regardless of: - Age +- Ability - Body size +- Caste - Familial status - Gender identity and expression - Geographical location - Level of experience - Nationality and national origins - Native language -- Physical and neurological ability +- Neurodiversity - Race or ethnicity - Religion - Sexual identity and orientation @@ -22,80 +24,133 @@ Please note that the list above is alphabetised and is therefore not ranked in a ## Preamble -> Note: This Code of Conduct (CoC) has been drafted by the nf-core Safety Officer and been edited after input from members of the nf-core team and others. "We", in this document, refers to the Safety Officer and members of the nf-core core team, both of whom are deemed to be members of the nf-core community and are therefore required to abide by this Code of Conduct. This document will amended periodically to keep it up-to-date, and in case of any dispute, the most current version will apply. +:::note +This Code of Conduct (CoC) has been drafted by Renuka Kudva, Cris Tuñí, and Michael Heuer, with input from the nf-core Core Team and Susanna Marquez from the nf-core community. "We", in this document, refers to the Safety Officers and members of the nf-core Core Team, both of whom are deemed to be members of the nf-core community and are therefore required to abide by this Code of Conduct. This document will be amended periodically to keep it up-to-date. In case of any dispute, the most current version will apply. +::: -An up-to-date list of members of the nf-core core team can be found [here](https://nf-co.re/about). Our current safety officer is Renuka Kudva. +An up-to-date list of members of the nf-core core team can be found [here](https://nf-co.re/about). + +Our Safety Officers are Saba Nafees, Cris Tuñí, and Michael Heuer. nf-core is a young and growing community that welcomes contributions from anyone with a shared vision for [Open Science Policies](https://www.fosteropenscience.eu/taxonomy/term/8). Open science policies encompass inclusive behaviours and we strive to build and maintain a safe and inclusive environment for all individuals. -We have therefore adopted this code of conduct (CoC), which we require all members of our community and attendees in nf-core events to adhere to in all our workspaces at all times. Workspaces include but are not limited to Slack, meetings on Zoom, Jitsi, YouTube live etc. +We have therefore adopted this CoC, which we require all members of our community and attendees of nf-core events to adhere to in all our workspaces at all times. Workspaces include, but are not limited to, Slack, meetings on Zoom, gather.town, YouTube live etc. -Our CoC will be strictly enforced and the nf-core team reserve the right to exclude participants who do not comply with our guidelines from our workspaces and future nf-core activities. +Our CoC will be strictly enforced and the nf-core team reserves the right to exclude participants who do not comply with our guidelines from our workspaces and future nf-core activities. -We ask all members of our community to help maintain a supportive and productive workspace and to avoid behaviours that can make individuals feel unsafe or unwelcome. Please help us maintain and uphold this CoC. +We ask all members of our community to help maintain supportive and productive workspaces and to avoid behaviours that can make individuals feel unsafe or unwelcome. Please help us maintain and uphold this CoC. -Questions, concerns or ideas on what we can include? Contact safety [at] nf-co [dot] re +Questions, concerns, or ideas on what we can include? Contact members of the Safety Team on Slack or email safety [at] nf-co [dot] re. ## Our Responsibilities -The safety officer is responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. +Members of the Safety Team (the Safety Officers) are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. -The safety officer in consultation with the nf-core core team have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +The Safety Team, in consultation with the nf-core core team, have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this CoC, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. -Members of the core team or the safety officer who violate the CoC will be required to recuse themselves pending investigation. They will not have access to any reports of the violations and be subject to the same actions as others in violation of the CoC. +Members of the core team or the Safety Team who violate the CoC will be required to recuse themselves pending investigation. They will not have access to any reports of the violations and will be subject to the same actions as others in violation of the CoC. -## When are where does this Code of Conduct apply? +## When and where does this Code of Conduct apply? -Participation in the nf-core community is contingent on following these guidelines in all our workspaces and events. This includes but is not limited to the following listed alphabetically and therefore in no order of preference: +Participation in the nf-core community is contingent on following these guidelines in all our workspaces and events, such as hackathons, workshops, bytesize, and collaborative workspaces on gather.town. These guidelines include, but are not limited to, the following (listed alphabetically and therefore in no order of preference): - Communicating with an official project email address. - Communicating with community members within the nf-core Slack channel. - Participating in hackathons organised by nf-core (both online and in-person events). -- Participating in collaborative work on GitHub, Google Suite, community calls, mentorship meetings, email correspondence. -- Participating in workshops, training, and seminar series organised by nf-core (both online and in-person events). This applies to events hosted on web-based platforms such as Zoom, Jitsi, YouTube live etc. +- Participating in collaborative work on GitHub, Google Suite, community calls, mentorship meetings, email correspondence, and on the nf-core gather.town workspace. +- Participating in workshops, training, and seminar series organised by nf-core (both online and in-person events). This applies to events hosted on web-based platforms such as Zoom, gather.town, Jitsi, YouTube live etc. - Representing nf-core on social media. This includes both official and personal accounts. ## nf-core cares 😊 -nf-core's CoC and expectations of respectful behaviours for all participants (including organisers and the nf-core team) include but are not limited to the following (listed in alphabetical order): +nf-core's CoC and expectations of respectful behaviours for all participants (including organisers and the nf-core team) include, but are not limited to, the following (listed in alphabetical order): - Ask for consent before sharing another community member’s personal information (including photographs) on social media. - Be respectful of differing viewpoints and experiences. We are all here to learn from one another and a difference in opinion can present a good learning opportunity. -- Celebrate your accomplishments at events! (Get creative with your use of emojis 🎉 🥳 💯 🙌 !) +- Celebrate your accomplishments! (Get creative with your use of emojis 🎉 🥳 💯 🙌 !) - Demonstrate empathy towards other community members. (We don’t all have the same amount of time to dedicate to nf-core. If tasks are pending, don’t hesitate to gently remind members of your team. If you are leading a task, ask for help if you feel overwhelmed.) - Engage with and enquire after others. (This is especially important given the geographically remote nature of the nf-core community, so let’s do this the best we can) - Focus on what is best for the team and the community. (When in doubt, ask) -- Graciously accept constructive criticism, yet be unafraid to question, deliberate, and learn. +- Accept feedback, yet be unafraid to question, deliberate, and learn. - Introduce yourself to members of the community. (We’ve all been outsiders and we know that talking to strangers can be hard for some, but remember we’re interested in getting to know you and your visions for open science!) -- Show appreciation and **provide clear feedback**. (This is especially important because we don’t see each other in person and it can be harder to interpret subtleties. Also remember that not everyone understands a certain language to the same extent as you do, so **be clear in your communications to be kind.**) +- Show appreciation and **provide clear feedback**. (This is especially important because we don’t see each other in person and it can be harder to interpret subtleties. Also remember that not everyone understands a certain language to the same extent as you do, so **be clear in your communication to be kind.**) - Take breaks when you feel like you need them. -- Using welcoming and inclusive language. (Participants are encouraged to display their chosen pronouns on Zoom or in communication on Slack.) +- Use welcoming and inclusive language. (Participants are encouraged to display their chosen pronouns on Zoom or in communication on Slack) ## nf-core frowns on 😕 -The following behaviours from any participants within the nf-core community (including the organisers) will be considered unacceptable under this code of conduct. Engaging or advocating for any of the following could result in expulsion from nf-core workspaces. +The following behaviours from any participants within the nf-core community (including the organisers) will be considered unacceptable under this CoC. Engaging or advocating for any of the following could result in expulsion from nf-core workspaces: - Deliberate intimidation, stalking or following and sustained disruption of communication among participants of the community. This includes hijacking shared screens through actions such as using the annotate tool in conferencing software such as Zoom. - “Doxing” i.e. posting (or threatening to post) another person’s personal identifying information online. - Spamming or trolling of individuals on social media. -- Use of sexual or discriminatory imagery, comments, or jokes and unwelcome sexual attention. -- Verbal and text comments that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, ability, physical appearance, body size, race, age, religion or work experience. +- Use of sexual or discriminatory imagery, comments, jokes, or unwelcome sexual attention. +- Verbal and text comments that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, ability, physical appearance, body size, race, age, religion, or work experience. ### Online Trolling -The majority of nf-core interactions and events are held online. Unfortunately, holding events online comes with the added issue of online trolling. This is unacceptable, reports of such behaviour will be taken very seriously, and perpetrators will be excluded from activities immediately. +The majority of nf-core interactions and events are held online. Unfortunately, holding events online comes with the risk of online trolling. This is unacceptable — reports of such behaviour will be taken very seriously and perpetrators will be excluded from activities immediately. -All community members are required to ask members of the group they are working within for explicit consent prior to taking screenshots of individuals during video calls. +All community members are **required** to ask members of the group they are working with for explicit consent prior to taking screenshots of individuals during video calls. -## Procedures for Reporting CoC violations +## Procedures for reporting CoC violations If someone makes you feel uncomfortable through their behaviours or actions, report it as soon as possible. -You can reach out to members of the [nf-core core team](https://nf-co.re/about) and they will forward your concerns to the safety officer(s). +You can reach out to members of the Safety Team (Saba Nafees, Cris Tuñí, and Michael Heuer) on Slack. Alternatively, contact a member of the nf-core core team [nf-core core team](https://nf-co.re/about), and they will forward your concerns to the Safety Team. + +Issues directly concerning members of the Core Team or the Safety Team will be dealt with by other members of the core team and the safety manager — possible conflicts of interest will be taken into account. nf-core is also in discussions about having an ombudsperson and details will be shared in due course. + +All reports will be handled with the utmost discretion and confidentiality. + +You can also report any CoC violations to safety [at] nf-co [dot] re. In your email report, please do your best to include: + +- Your contact information. +- Identifying information (e.g. names, nicknames, pseudonyms) of the participant who has violated the Code of Conduct. +- The behaviour that was in violation and the circumstances surrounding the incident. +- The approximate time of the behaviour (if different than the time the report was made). +- Other people involved in the incident, if applicable. +- If you believe the incident is ongoing. +- If there is a publicly available record (e.g. mailing list record, a screenshot). +- Any additional information. + +After you file a report, one or more members of our Safety Team will contact you to follow up on your report. + +## Who will read and handle reports + +All reports will be read and handled by the members of the Safety Team at nf-core. + +If members of the Safety Team are deemed to have a conflict of interest with a report, they will be required to recuse themselves as per our Code of Conduct and will not have access to any follow-ups. + +To keep this first report confidential from any of the Safety Team members, please submit your first report by direct messaging on Slack/direct email to any of the nf-core members you are comfortable disclosing the information to, and be explicit about which member(s) you do not consent to sharing the information with. + +## Reviewing reports + +After receiving the report, members of the Safety Team will review the incident report to determine whether immediate action is required, for example, whether there is immediate threat to participants’ safety. + +The Safety Team, in consultation with members of the nf-core core team, will assess the information to determine whether the report constitutes a Code of Conduct violation, for them to decide on a course of action. + +In the case of insufficient information, one or more members of the Safety Team may contact the reporter, the reportee, or any other attendees to obtain more information. -Issues directly concerning members of the core team will be dealt with by other members of the core team and the safety manager, and possible conflicts of interest will be taken into account. nf-core is also in discussions about having an ombudsperson, and details will be shared in due course. +Once additional information is gathered, the Safety Team will collectively review and decide on the best course of action to take, if any. The Safety Team reserves the right to not act on a report. -All reports will be handled with utmost discretion and confidentially. +## Confidentiality + +All reports, and any additional information included, are only shared with the team of safety officers (and possibly members of the core team, in case the safety officer is in violation of the CoC). We will respect confidentiality requests for the purpose of protecting victims of abuse. + +We will not name harassment victims, beyond discussions between the safety officer and members of the nf-core team, without the explicit consent of the individuals involved. + +## Enforcement + +Actions taken by the nf-core’s Safety Team may include, but are not limited to: + +- Asking anyone to stop a behaviour. +- Asking anyone to leave the event and online spaces either temporarily, for the remainder of the event, or permanently. +- Removing access to the gather.town and Slack, either temporarily or permanently. +- Communicating to all participants to reinforce our expectations for conduct and remind what is unacceptable behaviour; this may be public for practical reasons. +- Communicating to all participants that an incident has taken place and how we will act or have acted — this may be for the purpose of letting event participants know we are aware of and dealing with the incident. +- Banning anyone from participating in nf-core-managed spaces, future events, and activities, either temporarily or permanently. +- No action. ## Attribution and Acknowledgements @@ -106,6 +161,22 @@ All reports will be handled with utmost discretion and confidentially. ## Changelog -### v1.0 - March 12th, 2021 +### v1.4 - February 8th, 2022 + +- Included a new member of the Safety Team. Corrected a typographical error in the text. + +### v1.3 - December 10th, 2021 + +- Added a statement that the CoC applies to nf-core gather.town workspaces. Corrected typographical errors in the text. + +### v1.2 - November 12th, 2021 + +- Removed information specific to reporting CoC violations at the Hackathon in October 2021. + +### v1.1 - October 14th, 2021 + +- Updated with names of new Safety Officers and specific information for the hackathon in October 2021. + +### v1.0 - March 15th, 2021 - Complete rewrite from original [Contributor Covenant](http://contributor-covenant.org/) CoC. diff --git a/README.md b/README.md index 504929dd..b4605ebd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # ![nf-core/rnafusion](docs/images/nf-core-rnafusion_logo_light.png#gh-light-mode-only) ![nf-core/rnafusion](docs/images/nf-core-rnafusion_logo_dark.png#gh-dark-mode-only) -[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/rnafusion/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX) +[![GitHub Actions CI Status](https://github.com/nf-core/rnafusion/workflows/nf-core%20CI/badge.svg)](https://github.com/nf-core/rnafusion/actions?query=workflow%3A%22nf-core+CI%22) +[![GitHub Actions Linting Status](https://github.com/nf-core/rnafusion/workflows/nf-core%20linting/badge.svg)](https://github.com/nf-core/rnafusion/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/rnafusion/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX) [![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A523.04.0-23aa62.svg)](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/) @@ -29,10 +30,11 @@ ## Usage -> **Note** -> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how -> to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) -> with `-profile test` before running the workflow on actual data. +:::note +If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how +to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) +with `-profile test` before running the workflow on actual data. +::: xwwh-V+c9}3;WXOBquYF6T! zfoqxX7SowFI^~+T2%?nK?SE-fe5lV0aNpC~%OuN%YNhK(>o0DTT(!OXR{g^~){}o@ z6yXDKs&&DwsA&}B1C*PR`2bILFhJ2GztLNUMSpjBG6vhL`&i~ck^Tgg;utJas(Olm zt+v4BK`iDQ;EcECZF2iu@s(zRwj3l5Jp$X@7{v|Xqm`fbX%)FqaCviQSgN;uJ(0N@ zA6ObE{9s&cR#~m}y{q=XiLbwYS4jG8Kd(=_=B|;{pD2s-(YS!X${$pVF)rcmFfj|r zn{sQ_kLD&sSww|g!M4HMhxz^{#s}ZOV=Y#HCNUi%HY%57Vm$kRnVm*wBU>|u)j-9u z`{7~>1B$Wq2IA&{s=a*B2K!fPZ3|nv8=97{;Y&dwBo=c}RaG09)=b|N7DY`^ej)$g z5iz@JWC3ZTURkx65^T(ng0<0#<-_}??8hZo#1RSHS5qn$)de6dKhki*ZZ>gQX9v78CueMx z4w)#x%b-9IL*$jHir@!4+}{dK2+^(AfrxaQKO|`-<6!&J&<(kdMdR}l5wY9Mm~dKj z0O_)cFF*q&X6ud}6yh9b^^qU;r@u2!=QfSPAo(}N151S5??yo#3kwYji^op6sqdmh zTm%4NSa%S6IF7<5aJuhAY*C9H!36PGh_fRsQW8j_0V_+XaUT~gn=RuxNPHLCJj|(v z%52(uC_D%1`tsd_u1zc zB^9>y95Oe3GxIKH72oi?J5qXezg#UXNexLA4|o7L;g=Bus1Qh_i_SG2;Zoe~! zxatA=2jb=2hpBBc6*S%ay&k9tu+!96qU5c^p{VCql4Lq14dzIvhuWWE14%f3tB%g? z0-L4;_*bOm)Xw=0VINT{kuYTEwz*GbBDZD&ekrRq!lu~Aq#Xe=3=tAqk3N)ub#T># zv6N1kXmt$Cz)6SIP-2)(Y`h#~FBBP&;gOn2+;tsi=Sitpb+loTk&!(m$*5OQM9Eq& z)3>*ONN_k<{&|;3c)oh3ej${52P4-g%GHVkq zcp?ofe9W&qaB)FunuNJxCiI@(`8z#dC4Uc!g3!-RQB=vmwP=(2_o059@gBeYIj=BI zHOH(UWz6Z1M)`~f2|R!wgOs=X{>WlKs-9LWg$m_;=(McnW3^@UuyS%@OATN8+?n0!YYsvrC;yP4^;h zDb1~PF|fCbVr`ap&lxW|Dl+29gHP|-PV6!;1tx|oRyHUcd3b({31*b{nLAi|7+e4o zObm&SI669J4(5LvAQ}MxGMlcL&aGo(%YTn#cW}SboIux9njsI8B0O;_42w0=;SJe$X+k2^p)l3_mlyomn&R zH>TJ_ssqVBp5?tQ~R4EP3yM{4K| ziFqbjBJ2NcHXYHD;5JYmGKWqwyaf48;vsPst*oqs5TZ6B;_$;U@!2Wk>Y7&2V6i>h zZ-_T-%HRAvD}DHL?mw>Bpkm?azU{D2mno~rnMK{^9_wW1w#ED4w8^)#`EL*I_zM@l zfNW(YyG#nCSiPVRj-d(n(j%VrdMIpUXD3X+4`iv05%YIkGb{Me;a+Dul#=!UAohF> zO7d?}3=g_vOSrsDB5Z=_FO2(WQ=btT<2J5K>{W3s_og=0IwC`s{eE{Q50{$w4xzag zm0s<+?@jZRc;zNzOPax^5B6+^{SCU@1$qA&3GSRTOoTNOsKD?y_A5HaE`q%ul#eQW zsFhU`s2Kf9LO(lT-+uB7R_()T7cKNlW0-JJCW1iJo5WMi4dtBlxv$J1WLf%fQUENN5m6B83w*^!5nJe~I`De(&msv$OA zy?$K>20@f0Ro48|ANGMHTlt2Hn`)vQI_KNxXS$^GUe#{W{?z^Ey>U*BLtW)^0hw7_ z=G8ODbPeM>!%XR{^~u+TGT_RBpyed5)NkMMUl-!uktSo?A;ste(hWt0fOODd^&2FM+lcE!X(FGa>oW6s;&Q>=&HPrM zw^y?0b`+&__W4~?d|E;ibHc`hf3K^7|H66C_}G3~eE&_;vTCWqcMRuNw`GL4 z@!xFuO8|^{mw%aX=T9%i_dWu|gNNMOUsAONuD|V(gi)w@bI0&(#p?n3NUJYLC4VyG z#Ost(tuwLhqTmQ^T5Hv*<%CK^7c~YAt}z? z_ijJT>$mum*i!==hbrbhu6jP8soMKdQ zvJb|0&HAWo$LL1if*O$Eq1*&5<+2<((jkqb(PQ>W!Ly6**cRmvF~_@~ho z$r|zWuUnJ0=Tw1G1$&Q%vhg`)uY+cnEXtebBLL)JAqzF1mabYU?z8dC%tp(Rm_0k7 zfPWs6WDcxaSbFggH<7)RVc#%Ijj6eL{iDk!zzz^$9fHJwmcc0ck*5eMrMTH57?DC) ze3)(b`M16NRGz*SrnkqgE>ANUkB%3P{?uJBiH-)n@t4i0wG`)vN)c!T|8_Oh+V*^H zwX@gxN|!Z9?{2**^F!!fs-aT9tcb+vGnO>%9&9ofHDE5N9%O-CSph7|$Py-E6&&}9 z1yvrD1&^MhLnFE}*lNIk;Rb4*ZQw^EBA9{eY=xU5p4WRU?d1yOm1z?c#9{^b7G@C; z0sy4I_U$mJ2pw(VutPo}zN*AQ(+Yzj$3+&&D;lF9#jukVbTJSni9#6`x9H(mscKkN zY&gBE=I#EcbigA_ud1exA9l3AGF&FJ@s-vQ<-IqG*@F+8I~-rYp8p-X%L4C&&&R!s zWw1qX^5s1E&ZdNq1!#$9&bcQo#E1+xnwSjsLhNGy#EmihlZ|*j`g|k+!c1m8?h_zw z5=5kXE6K#|Y3#*>QYIMWRMz0KMpsO_`k=l{Lf5Pbm(J&tmXi!J^n!hBg#8p5#~h~y%Qp$8WE64AI`Ll6QkY{e1T z(A6j67xbcy)F#q*l$=ci*1t#NQSCzgaO(!Ot@$DEnq|9Xie7ih zmhLzlkY@a#s(FCV1Xe$*XaJ&-Pdtjjsa^oKnPW3*$w!wlvVTNtE_S9>=HL;OuQ04k zgpYWtQJMQ9hC(9qBSUVO#pwEZ(aLQOG}aib82j)niIkim7{FjS+n*J{JacN(tRx)c zGW(rN3^CqT-ctmLpq9&y-Mc?!v*c@PX=!zgWn^T;{kjG}HQ4rvVV>XwXfi8s)*AiP z6VGAJczuAE2f_EYk$k3nE7#P7A9JSC8{^ILu((5xO?#e%0vFEx*NeE><3Zg|O7a!nL4x1; zCz>LxQ?n)w;ZdDiCz6``>$cJ`Kc>U{L_#* z^y6CyJ_(gTyAhNyaEe}#K%&xbf=$iLi1u-B+w|CG?4Z#G1h&hfdK1=kNb~GjwTXOB zfaANcKZ@`m#4X)){O6G+RQci1M4;;Jp7M}&8P5Rds15sJZ|_0hMIflL#d7_S?;$Gr zj#Kvc-n>7TV=xuIal`X|O-dG{>E_Y?5yR`C$D3Aj23N7wAOA0UKU4q1#4HkcS9z1( zz`ff#?%hqdw0j^bh2GLoO)yzs=rCHMoj>A* z+UPBxGVd!N;dd9_j8ywBC^F7+p86AQcggcw3^NQ24haLM9hnj0O^vx<#qfvo_I?Xs zEgRKC!$m&nJ08nCUy++Y!F0v7>p!_MTlW|y&t!5dXVl?I3lWMFF%(4hKKHlund8H; zU`G@&j>IrXVh8e+L2X84qV~@pCBr<hb**410-Ay)wFXy0`(YjFxi}M@Hlu`+u;l zH}zj6n<}iEd3SBMwW7Z2nPV$A=reY-i7H_JOvtw!slh&)pzEDjAQ& zGz9|s2jI;4s9(tFDtqv23kJ9t>)RD66FE6LAI zVSXVMD*=t94J0AwoE#KTx&YgN`6#Ias85Tc^=Vg zkQBkA+iYYzf1bM3Jtl9ysd77`$qX}IvO)n(QhiSomIaeBf=O5xC&3h?LHYv`uKsHK zbw<$|$}M!u@kB1azN)A#!5xAQVr8*sdZ?UO>DM@u*NsX&AIn4ada~4!N6xpZI*6LT zC3oJcSOUB@psQvTKvHUI)xomc`1v6n4N!}gj7Rr%`uA`b85{p>S7;KyFNQpyagnuax77lKSJjfy6 zn2Kn-lvwpl3nfX(SrmJsaXNr&E(h-1-+o^n*;NWlb0`^aHbNqLUnj=;%vQd?A7S`T#v1^F}| zQJ*YRa7N0VX%6iM*N)gaMcJ}v&j}Q9AF!`bucHVs5iu!$JW%jy6sool7A?ey7tfJJ z#MIwNN<#v^Pb!BGYXR#bA{t_?K)x?xz&*6WzqLEgcuYkRTVjAlK9W1|1k+3gWDX~# zEV)#*f}yH?(*CB5MOm2CY=T49Mv+>+RGI9MKR#vv;a)7%{janQQ{Q|&g87Vlyp)SA zy@k?;+DcBaq*MdVBYJYk#Pp$iuE(zDE=OtW6Fp=rD(Vv_*b@?{it~V8E>SuoI;8)6 zOF|l1#h~L9%(goTG9^+;UQ!hHO!Dh3bhc0~?7#M_XlcHHckMxlsozq~9D*v-B@9FQ_=Wb1O9y(`{Zt=_gv z(5r#F;YYA(|6G-W;6@lh-?skjN%Y|OIUB#g>PLh|%P3AYzntARl9qH{W9&#OaxrJt2Al69E4oD@7H%;tnZgMH#ci_>J;+*Fxo0p&M~`HJ-@ zBN_}{R6*VQ7?s<|Y3b^wl7(?_x~J8Y=8%Cy)3me%%7!xWqTj0ldjrQ@^#^L&{p0-HcTqa2XwIqd1ZQFl6}q3 zvgz&wDAQB2{_fh@9kymzI6?>~{3rx%nI``~?y|6RTxJp)g57eoCZQv_B&eAfB%?A``||N#*OQr zEv4OHpuzc+_n3jkImO)f1}Yz91a-|{t7OWgHf`;XzHZ&q@H6W4w@QPf4o32CecmqB zda-na#tqf;=AKRN9v641Mp$?n%X$trINj}^ly;Ddol+fMTG!+LBVY}0;Y%ip>5m^j z%qeE3rX2kIv^WOX*>@_xtEtRG4hwipTLAI2_x0(0lO-|)T_=DVnU0JPGZ3>P$6VZD z|J&I;>}w3WST5sEaSh*<8={_BdX9g-F4AT{+9}V9?q>c+^TdnG;^bFxCFt!#y&ROe zdOS7eVB?58j6GUr}Oo&;`S+B0Jf|8r^vh+=T_R! z|7)-A^|1`O+N2%t<=s}6GECan6&$JNzYSMfgu-QNTeY!0RJu)rr)z8$U)$GD zGr7iW_fLs7jR8~4?6j!#C_=VZc2OxqCOr+j|L&vJ)O1pEOUxk>?Z> zqhvf=QKuFw8-&zxte0iy&Yc%8UD})c;Y;=1-C?b_`QBqcdoU4sJJKhp+^FiJU9qAZ z|7x4#2`eiB437mOC@iLe#&s17%T<8nacGo!-``(hV{5xDx(7R6%_-I0U^}iyqEAJZfGr8B~+Xf^I4X%t#rN_fZ|?&uVOMQ$FS4xJ${z(wq;_~nh{n5v z8kXaZ`Rv-posNrM`G z8!NZjxr9B|eKNJO85_BeiJ9j=ai4+Wv5`}^A#N}Acfj$_o;@=wZA(yIp|o$Go>`nK z-_0+uuzP{VpcY8xme!ie%4etnnAWW`8{U*y=MTI0Q2>IE6IN@d;};}g^idLc@yqJ% z0s_xLgy%q|aM8uZ1V?G2%4l}+{UfJ`ALs4*#;|g%FHCNg5mR@}&6pUw8}3?80%3GG z_qV2Hid=qWg{>wBL-P`zPXL=Gb=>Y_ujHeWpM-3`<8n!1a{gbEU$AFKX9D#q@^?%( zeqR-N=rg|aOq}Y5LIxp|s-sRv3Ro9am@0^B43KvWbl{j}#DFF-$um{8WloZW_`UPI zc;sPU+=bh_-iF&b((s)|f`!$VyAq%g7?zQcsIt)#`V|dvB%++RLTEB}qAjKAAVYD7kp$dL>)tzYP9T_raUm_Rl@Z_aC}kKx4+3 zce+()od-pC9G%CG@6~d=bTsD@Tm>fBgSa+OLm$C`y*~26d61`}a z*1eB-9lclV%$Y&^Wv%RCQB|xTQqEgb($+!07z`1wwtg@KuZd`^C6Njv2Vp5u`Rq8= zS=dV-V0Wmgv&vupZU1fKGiP?{9%U^(Lmocml07TGqT#Bjy~h@x_&U4A@O+9zq@Ssl zw{)P#8Tp0#t#$X_;Nm~$H-ROQzau86m2TPY#v+-bx;H76UO)Pb`{(*st(1{2dJn{sN!GYDd)vcznAt4;PqRyk1+jt%ndwNnK9Nx+J@^l4d zDJp42S63MbTz%2~MTi;bX#V=qQVu5Q9Hw8lh(F)DmQHf_?!=s&+Rlwf2BXsB;^ZIG zdIkmtDo@@pi^vrYF0&w?SjTF!l{B=xOzx7Q8Q~jqR_oh!xjvLM|dVFMC@}=m9GgY3~E`)2myeoNb zG3ZQkvOr;QN?+G*d9q~>#}u5_bvXmNE;&12y)JJmW@u>Gn0I=OT8vBpbl3Ed&0JAS z&b16i4gM9h$_p&n?F`CfXJ-$kwuW4NeWmdaDLcFRwpRHr(H>%rm%c7^m8PxG$){A& zGMC<)s$yzLH_F920qJpt-0VbR>s1RyHvT>>Z;G!1p$jVRo1eW zHwRI+(c$@t_t zXlng{=yLAZ!GK!+1#T(Rnl)ZvO9G?IuzJrGo^nG&7pH5e@y!n$JScq07X6*F$i1Yb zJKDc`RPA}x%+2{=gu~lEJu{OT;_Gkag-Woq)88fJkkiq0O6cZT!|cK%EL;g973(x; zg zc&{ok=80Xj{}k}!JtuSEj@;k(AKn_kA&#u2*K&!L^I*q99vLVU1Y9$7b47tN0Z((* zQWWVw!oT*ceW?5MM|znB3M^H*`T6VEE~o6vdGKH_`d%RfiD~pqkKCrj5!Su%@{B=h z4vmjz)m}1VxaH&R9pPkaV`JmvJQMlsbl+;GxKTB^BKNfRybJ?GI;KAQM zJ`Wyl-J9PQQ@;lH15Yr8Fg&ug*~+SM;4+p?=Q?PQvN%jOth+GSD%St2rJ-R1BjH|*w6Iu5=%g3_-Egun zRjly5Cf6K#3HGH3`q&tMI98U;{!+_aG0aA8WMD02Ac=L#-&;#qQ4YU^ecF*!)l+60 z0S4$gnb~e|IDdYhsp-AWeCfL@(Whjc6>pzy7-ZXh&#(7xbTm&cq(=vaquV^@*g)W# z_fUI+Hp=ZQE-BgOXlFYHgyIxRfl#OM@$m`Bz^#1dKerasRdJ?}CaLA`;sRY<WLGWUuD!;)ROHOSog6%h9D zSfiFb>BA!iNjDohO1X%PrUxd=R&JKPhVi$^4Aivi5ARdzp1?2?$}Jykjb98Qx>Xp> zIi#+4Y1TM{k=g1nP0U!-h<&Rj80KIG8rcLi@Sza+4CbJHmCfjW8PwF0&O9D~Ce5@S zMG4+G4br{DwnV+mSMdGe>34N=`!YCqram%X`}FCpveQF~uHcx=DG&zzPTe@1IE9|{ zMo5S!^k!dxI-%U9!9F?7bm;U~#Mr*yy^>FcMEOpxrGVwBuG%-yUhp!A<>l0NcEM*7NQeX!OP&TCLbydm*QnfI9TO8n%fO%vYPvStNG?z| z`U~0>N!JM>cr{!>Im(Hsi*6nQ0$)Tq85x=Ot}Y*7gYf`$dRt3&8=QEzS(2ZhU*6oj zyWA*hWY0HzM})eqzueB8In(&*Q}~p-u`wrlCqMKJ?MUL-u&@tB_0_<@8&OeHLnALJ zlywCK1(vmOivS@7fFz3p9EcAkHr0XE5c{mG`tS#f3gM*MN=i!Jnkq$aXJBCPw)*5Q zmUBP9D!+K~q8(e^lZ}M;S&H%#0*E+#0d#JPh&m^&t*y^9a;3SLsx0Hz z+b_@hD>sLJ^2u@`Qv;u3_Z3v0UQfoBH9PX&uU&sS&QV~lUxZx5`5*0-{{rPAC-apY zZksRUpBvt85;qV%nslp=C->Oiy0k)Mg(saoJ%{S~!&J8Mq;9<7|L=&GH-QtT6OoI;dDdNSv zam~gkYHxieD>?$cOVu!6q(Schu;oB~Qc@Cb&SHr(t+z6q&Ypb==LjC%4NOd&y3|xS zaLN}iN;VhRT0g8lDbxM%N5;*iQm7!KxCKMZK}0~ znV#lwv?LS8>kb?^;Hi_IIjW1=fq9qZDv3YDuD?`6mxStvE0(*iYL1} zckh%{ROrAmJpf!y{l{zl&A$A-qSyUVH0uFZ*O zp!(Q!>1@}3IL(chx<8nL{)BWb+mE_M3R82J6r(stSm?inFTG!+&Ll~z(YXAbe14!* z6c_$;yw(Yci949~KZ=XvAeyF!SvfhYFtx6P<#ww!ZVW(fHi1W5Z&U6TkUYUahxZiU zi1dGP;PBxW#CZ?*5puNk2-_vPa~_cq5&qaD`UN`La8p~T6k;0=c1 z9zKcEN)ixoq}cD@zmGpOU5=v^2rQ*ibkRu=#OAh>1Oq^A&f5=8fNA}DUxE?WTJAEd zkKW_59_|z1#nRusnRvcpXN2~#4`2*QS<5(Ah|Rn<7_5?;9-@|oMwNUlNSB_&C-5D+ z`BDD0$4i`@e8lNB2h4V{JTQO9g0)xLr{O<_(d-8pcV4n=8F36YKNFGR4*->egM$j- zm0?l2O)xO?r&pG)AZ2`R!5M9Do%ENmgS>{82M03ivvI+Gq~Mc)L(Cbrg?Hg*_I@rB zMHgpgACW18*a~s(zSq~;gt2*TEe?W!-+j3Lm>``!0^=1{xB??utTriW8MKBxrq*Ruz#Zo9& z8Q>_0#*Uf_AlX^O)@W%bI+zxi-?avk2HN^g#_6`wJ@fet-mSM?N05-DIEXUT}zkipA zGC>v&U_Bt|uD~C4ptfCCT92UEa-mHbguoEeW|Uh?%pR8^FdzD+wC^l;eMRFzz$!SY zKt^*Z>5WDGE|wq~kEu^>XJ;mzGW7EM0%A8S)mr68NIMCXPW{*&7#$uReGPxUNyn=o zX0TDp)jSE!irt-ylmppoPh4MpdvEyYwrh6`DmHy?Z_iq6N|hnAKclj`&`xo_x+O(W z@{<3_Hc5{zi?izUe%B6ek3XvhY!;u0R+P2S?hO3`mt;s=kz9OOjSLF{9)TJB^`E$ zLb=E-2RIs*nr*rqXuj$v(72VlzH56O&3el);o17l7c#qw|#_-~d2 zSoG&dEj|6N^l?-+lJ3)zNQAzib19X^sFg!rqB7J#L06~gpW;iFX$s*P=UR2yOOf1k znL1O{@kf7uAj)PU2~%jmlFeysjnkQG{)L0ewSD_rff6f`{rmU3f(a!{qBw3@(Vef?&0mv)3)W>Ir`dK$(2Xrnc6?})8H zdR7p93o*NWgsNb8BI<$&nM)!hn9Y(Qwi?>c@N*3MObWTD|^3r3uaqAP$+2ym>-RhP#J4=yZul<1C z%`~XMAaYzX>g;yE=I8LRg1LFp(whNY`0_wgOxmAbegsD)pKJ_dGE1K2K!7lNsMg1) z%ax{UDzcF{k|3%UDl{~G`eXvx)J25(90VaNm1TKn+SrK$%aG6~YIk>cNypw*xISlp z{W!5Ad8!!m@_kSM6)N9Ur@$=Uh>X-VDY#*a5D4!Iaov$utY_K>@h*GK@|;l_U8v=HpNO!T94uWTCu?sjK7 z_kKx9NhefJ00WdUkzNbQkqmrRhDJw`z}zM-!L3LepyVp_I-#3hRaIBNA{PV}PFo(z zO#TE7pAZ-Kv{SdVw3JUGI894k_A@WsdF;ziBCkJyd(EyhtqdBR8QxS7~4hVWV0uf7U(pJ|IRf8wz-<@iTj*cFJ(?&apU|$8z{Ib@S^z<)~e}$oo1~y-#uzYZ2 zbo2!}g&=I6ZA$2=7l+f&Q&NEfgFoiDHf_HDz$Cf}#8R}RQ-=StG|A>*V8241z-P_4 zN%G_a$bI+kFNT7)my*5#$PBXZ*`8{)@1Kj-AS#4Si{7iVL}c0$!)%k5f%kg~Wz2Z_ZEnI5PCo?-<_jASxsrHegx zF94&z46fj&#oH(eM&uS3o8$dxv)#X2n*nDyJ3VpkW{&}UjNZ3VlLtk<1OB7I40pmT zAgYtZXaXQJUjT#=8GGQ7v8HSgL!}qLPqm{iB$#}Zj0GMw3hzwQf@RnCakpOTVY?X{ zh2;4XC4;ev$x1sE((9u-t<2w=k~WPEypCb>w;_H=_ zm4hk2Cnh-16k&ViQ%ptR_2fYLUpdn9`7@8T{{cPZIno0|zcLLO`AgjQpUCvur$>0s zdIXx`oeRM;2F{faKR@%k+S^tp2ZM(Pz*4sxm2N^;6ee2*NDAlhB}V!XQJLQA0yRA*FHG&z(IhkHIuy4EEEr%S5m?(LSBY z-icw)u*qD~NnqId8hE(4d)O=a_~Xa4Ggc1@3tuC92V=GMhODeC^Bi1b`(p3 z4iN*FG3(R;VedX*i1BrnZ}w~v6I+MBUnBB;3OSrhPHr=LxUk3P;WdfV9$qPZ$dz4Z zA0(}ngAafn;K(|m>SiQ`2kB#VGLCE7y@Nh1UMKxaZ}0JM#Y$RJ{8#)ImAP);<@IB_ zdWzb1EM}VBmf@w;@3VHjTklCMoDR(sFBfQQmMs3G-{qqc8#YVC^kr@Kd(a#6bl0y~ zI#n%goJi#(M^-~ScO8XJ2kLkAk1~(8df>()eZ4J@G8*{)oj|NNdzLL*7UhbSQ?EiT zpu{WhoI+g_N^I|*bxw7eMTuFj$2`eWDvfkX#hW)*4)b{dR`@G_Hf5?I=ayB)SufnZE?~;Mg3MfFeM2MPdQX#o=7rOczu!=hd2nG4B*|LTFxp*R^I>H1Gzm$+h3=9+x zxDTZ9tE+#IXgLpk{gFKYm%em%F2S$IX2m$Pg+sr7`4tvQn2P9{bsS;S341n<=#YST z3+_>=NDNItnC#plbTqCg{Q0cmG3k%K@GFK#`TFhc(`H3KV@1GED4D~iO3`~G4sDgH z9p_NjHR@oXp$09s^U@4{G4HawQR22&5pdaXPVy@sV@ig0BSEoKKO$-4pS%Gn(XCif zmHFUBkFgmVwCc_&V`F2YH|Wp6gz8OdTU7IeGroB7qK^nF6Z4KWONx#;FpJ#uCbY}) z;vCxRKhR5k{5353w|zn>XKMTp#{Z&ZXL+-->+HqOeM~LCPdtAp6X$w91+@77M^Bz~ zVB_U0(jLO6j%UjjZX9=DmX?h_CXEaG6n=F9FGeiTwFuDA)4PiG^7roF=iIYr=1WhzSIz#T zUSB<(9h%5{H#4L>W@V5FViZs)#C6u5GM*W`u;ye%F^!*dH$$)9 z9llS}ZJ+*8GBJGry~~e_CGw;$rf?jaK6kTV{PRjU5uB?>%mgDd;|%<%d%G9 zk)G30nhjXZe8d|U+lsp5)yu#Qs`WTUaVW9Ef(|H!AuY83_^}c|VV&liuHIgMU~n8l zLK^mQ1Ie-O3Seq7ZhdEDq-pT4cnC4=186=;hl~UO$ZRnh!QSKMr(vsQ8dPVQrw}>? z#tXuhHQT>wJ+FfA@aHG&^}qCy!Ba;2IP?hAFvYXXmLFoKl#gFu zws!B01nyN7T>@n-AR!>#_5o|h69JyE$VcHa01YssUtz2P&}^6Wl>cGAa<~Dd@0Mkv zoC5oN5n5^0dVu!V*!Qpy&;`t$7fL*Zc#U=xN@%PV(C#AH3Smva_vZX6mCAq$1F${J zyU%Y&e%K+c5?1u+k^l9fh_p`N@u3;1y-%a1s6DzzGgm@_8zE%87zfvTGvW-Lpiqgc@HZ>TyErH2&^}K0Gu|$U!kq4R;fKs-eTQJyzp@r`1cLq@f#aG96Fub!BwtRs=VpQs1 z!P-Sou$nH_zv2kgyw~AH%(*k$?lZ072b$*SyLY#_+1h1|-e%jfC7JH-X|)n5w;i_V z$H}L6l)g)GIhG9ZTEKOAC3Pkur(-fBWDdBM>gAXUc187>R%Ypjn!5>TeqxtNNhtbjn?G zsWs8YkKq15zJr^#?~nk2g(4)+o$~Q*LV$)1RiIz2AFjs)FdB6U9qC4@Q|67Di{u+yDIe zb0H`kk@~sBJ0hGGv;aavtB|^aOFRGru!z>`gnvvwrv5M~E-p^NV*UE{D)rE}Y4^TO zyCZBugXg4LU(sbx&niw2UW`jYK>_m2^ew(iAsyD~plJqtymMYHQVe1H`u@|^Jq%0~ zRgo=>jk^ZuXYx%TP+NVzJbucRvqY?EAv~k|e#rBWcRi^b`IV;myFqkZP9Zgw<(hHY zszp>GpXBSzJsatjOxNSGp%VFWQ(g8lq!+!CN?L)_n4N)Ix%}EjQ4R3U(kq#?Zvfju z!r|>F=s(RE9V%Wct(kQQw(X*IF%LxmRfa_ZG_A6 zP-#)+S4vZXQ@ow=tv23E*FyJ-)aKZa8tsXAD+EVTQdSPrPt-~Oi3uHJD4NV;_16dO z^8)&mh#;ly&Wu1}DBgNG#6{ePv)?y|{;~I-vu9(yp%K&>sABl5A;Vq1{a=5gayOEi z`Q_M!j0hT(cjzG@CJ!V=&MPTv=Ca+K71dxl(i~Lr} za@0#pYleKjTnqK@O(v5TrOJWpjWL(bXFWVToCBtzGKNg4|1Jfd*|`xXgbWIsvYl-0 zG1j~>#?E6>{TlX!emOZpxiy>M#8!#4*n^28cUQDcIi!8yF1nK&d?=Sf6$1qYbpKTJG@<1JZk2xNct;IBN>!eW;k+%;=g~^y{~# z+`)>!3f@eFSa)Rh-#=oE0RcX1RL8s{uD`p&Yx~DSoiG_-xTo?Cn)q_d?Q^F^0PACe zC8a#*rS{L0TOuCF=#;LMflt_pE(txoTomc>ZFOyDjbeFJ0b(NJ&2GX87)FdJLln z%jn+4x1XOECM4)3qf7}E18ArWXy7b{r=gGb%kBXy2nhj!fQY!`#$GM~eQ-l{IB}Ja zG5JJ|VoAZ#aTf$aR8*KlZNvitr+)?I=fRktn2uGreya%tBN^0Lr0Vkkn97QrF1)zBKY~ZY}-FrsRy) z4{;5NJZ;mLO%@(3mxwB-G}WKv9;SLXY+FWQ(B0MN1*?H8`f8>#!2qsi;4e4p9mo;YCV2u`}22&=AJ|5=a17RNpY9Ri|9zsWg zH8*-F2*5mJ1d4^>#&*3+_(Q?=2V%DArV|k2X9!9s&zxaIq`;e_vppypUbNXrL18h` zJHzxN=`ewOAHb)!QV)RvRl+GCZ051*N~6p^Y-=;0$L}drJif%NS@>yIIAzM};mFCs z?o(r>3;A8iiRm98uAv;+^xS#(rN`7F@-D`OIrLNOY zqkHpfT0(HaAu>h#={JT02<@_I(jo58BD~Hg+CIF0DFBj#Xvi)iS^-G$HvK1~fC(;WkrXw|5|9#5a&;QlO6)6(( zH*fBQwkCNumHdTs+4!6%Kcq>Rs(g!LpKrWUx;M(X%b>VDdS4XTh(J=u`BsHl<9{Jx z29|HzowJas2sH+nJVHBLgFgql@j&wki8JglW-+`kN(58@N&`EwB2Ya}OJIk6eIgPE zL`j~Qbb9vHVhz(wB$s7QFYVcFs(H@pss)eAvj6Er)2tWzc`fSH+>w(XS-rTU7sB834<9~o z^}r>2DIgRjOy6vCB;yu@l?h~MAW$OU&!Lf#^r3uladE;jkc$rWiaw7ze)R(@N}AP1 zD~EIE$YB}+XBdwJwqDNY{*h<(_4QwSfJ|^-@Sqvq*A122h6^2L>A$ob6#tE6+<3{a zv`|oNNfmYQ9=|Yb@Au=YTKq;T`MUF-y|j2uT7EFa=y(&h?UCcR)#x~(jtPX|2nJa3 zadA{o$UusI<;j!Xz`5JMe!YSZl7Pr$`TpK=hy|5AXqCibRPH|qR6PVok(01umtn8!~%&=|DF$V3n475@y}PRjm4KlukB0h6eASea90%*3yWUYtcN(NQV{bU zh(_fQ$ek2A{^PQ^NF@ClzQa$!PZAoR&>&(9_5F2;v8ftKn;jIUU4n%CT&Ag7Bj(j6+Ha zYppmI16>t|v@{DmPKdM$U@y=QM3J~7srWx4-G^R;ycp;ad=xZiMfY@cbo_+l3^#rg z%7gvx?y{ZsQW(bEv+!wmOT^Lt>Yf}eQ7gx zEsuS$(Y>s(#6^O9yj*cbZDrKdwaMz6ud#^H0SE|dMf0Y06oX1&1rt+JXzlFm0N<}- z!*Noo3*(w|Q)dob;jLV8wl_e(nmeO8cPe9fj??)(EA@qsmhd=b`t_PWa~3y*ly#+$ z6dpZ!es;vW)>xcMqkxS+PY(H!q6R$yMEWnV0Mip^>dxm1)6+y2g>Ebd3kAvO2O*EJ z3+=F-6+XV|Efrq8C=PVLOq3;b`uuw1z61wj(rP}WGniUg-Sa>CqNPJWbN+;Xu^^^e z(=Iye!OpIgC%!H+o>?THqf8LTXxd+w>|i#%dD2Qy58tV6TRSy z?B)IHTE}pQI*=qPYHBBUNt5zTkWH!Xy}N>_fxX9O<*ToBzTJ1s+us-+pz{sQ)Sn-%@N<8Yv?fDh^_z9| zpVYdR7_@R{$WiexUbcKdU5oVuWbOdwFxs=73=83)awB~xSO7kOXfhtzl@{eI+SEEM zO9A>Ecjw3&CT+CR7DA}0QIF$GXkPg7c`rl|m?*lAF_c&hfv^N_rn;k~vnF$R=+goL zo*PS{-i6A=7xGtQ6fVs0!ABA3gQ&c`3Lkh~q9r>`7j@+-dYf~keM3z- z1o31Cbb0>QukX{-)5{-)f`@1@p(YlFjBd2Qfst-$Y_O72eIe+^VmSXI=rgh1*8{si zjma2p)T2uKITNG>Alf2WJ6?CZPT=pNqgM?CU&rnBB4rkMx4n*=xVS_V>#pvt<@I+t zpcDUaso;}$IeKN+&lJ;#?=#ft-~aE$Z1@j>k?U1a@nOhaI;y;zQR$Z{Ls!4 zZ$Kk;H~jq%TuE?}i=Sb0YT%U68|+#7u0$q%Y~eLvL^d5D* z_Zr(UJJ7%Yz}S%kO9$-j3cRkQg(G0 z8D+V5ktuxOa3erylTL+vFf6-u4Mx=N6tLo_${k#Pqk^C!Ux z1D)~%xT>{c22~rZ$+!_&l=nY~AeS7x$Vg9(pJDa}fOh_~N=&uKEIq=+>#Ry1#sAA9 zDSiL@f?qJ?ZMC_k!SaHkx+1#o*{kf5oLMEYXUM2mzyVJqVPre8*8Cq^?*WeW+y0N= z<%5do8J<>G!QjIx{UF%??Au3Qggz%Gz5Dcu zbl(#x3$j=xU&PP-!jr3e&o6xe&fI%IMt^|-_h~`RNs^nHj|_5ZfIx#vL1-LlqSNL2 zcxyjSIsQ~0&z>vK5lVZ|*jjp?X~;qI z$4{W>`T!}c!;ec807giTKcOZ!8iZcDu;*|mBWx%jKUG6=nALWJJK_usdPBq3qQ8_M z0fJDIe1OMi4;#OwFDJJb+}&Qlg$=v)E9~bbNIC66e*|~HAMOBPKb(NHc+bIu_yy0a zYHt1IZq$e{3{A0XG8s4z> z!6(HUs_u6F@3~7XedY|hvgoIiDa!5p>-d@Yl5<&t5Ywx~KSj#Egrvfx;T4ag<;Sb= zl;F%81oz__D2@F3{rfOvd6o}Q>H$1ykHQ6jZILP_4~$O2Crg66GNk)UPtW_Nrh|ah z153zdsT95g#CtTWi$-2SII}bS5`+-w7YRXP5BZuh)&p|dQkWJ-tW7utttwfpK>Y_R zvJY@4qbS>I|Mcl=S6=fQUzW(Ocq#N5fhEAcyeXVld?Uas1@U{F*vBkq{&#a}{%&2| z{*wGx?~dGx;5*U;nT$eWTjtv&n45RhY>p59}pKG0~p5uUb_%N}2f2q0OV5a%fPp<0FnK3tpwzYEU&!OR_g;$(RJoQF9@>|G| z9tS8SelOPF`ophmqdO^`%XbdD?Z*}k4jibehlJA+m?Rt$65^DaEK$e-7AfOgY!*eP zoGrkuJ>rK@i43etA?t7?VvA`A!55GC{h+Q~dV*wZ zvp5Nq`$^uI14cu8(d;?sTz?nHhpPzbYax>{h*RurY%)NYKuTcD8KtE!*XCA}4{7;b9Bl64^)Y#ViSKs4cq&sNMf~#Ok=t<9!7ZCbt{L z?EY#oehsX#r>}9^@=EnCm@Z-I-JI9p$iJBwCX)BZS;MS8E|Y5UWnC!Mn~Nu(hC<>? zaZ(&==BYgWPo+Rg`U!$&eiH$5m?O=w1suTw3YVH%tu+dqvOl0qh&*<+Qn%1;?y_G} zASOiA8s#ePON97HS5NW+X|M1hb1#@yNO;m>sK z!r8NT`G0hqSKr(Dz%4PK7LJOdPyB8djw9oZ0C9ZExN-R}|sKBCn%n_G4V_2~*Q?9Oa9aM;L1b#qDYAc)P)$47}p9w zTGnspH#5@HTO!S0XN-@JzXF}VHRlaaHtpCMhw+z(H&{m<-ohZx){k9{En({qUx~g) z9*u-`ji&i>aTm;AYVEaVV$8(hkd6$@{?uTR?N*@piUq{98^|#g=rJ%Oh_rkqnc}0O zXu%RQy{Je7`d1pTs{js721*V4i38OsLuiY`xDxo)vvJR1n8Set{{ucePY8%_rUK1G z2ij1AA=Dhe4MOjzB(Z+a10)CWbF_4H{HArs%Hdp)_3o0T0#KiSVQj1t{3r;32WLVK zss=3pp2$|v@5| zr|=+`)NiSJYkF`OXI&8DVD=S-`yAJHchak&-Oog28;vqJD#^<~8@NA20hVyplDMTk zWRGaK1>#JZXCQlkA^4={w|B`}gQQO@U~ipYzF6=Hf+rRXWOjoVJX@v2#>@vj*nzA! zUT49P8|n#?*m&y+WO=$~QjB|LDO;1K#nfEasAkGS`)Ii4C-9y*^nAD8GEwj(Y83S0uRran>otQc}c zWbg&Dv-Yu|na4}@=)skDAWweh9uEf>mm+NRPOuZr0z>N9A$uU9~Ha3#7n-)b*nLhD17H88kp!xrKFA@L!SVm!a|8Sj1>?*cVG7Acn z31G^qF(2WH0zruc;MLuM3bJ%Euin_zp;rRN`#J%_#sL)w#KLOtZvY)D$#CsE^o=8zzWs-8G!YP>4KS?$r8HNVc?=lYh4Fk zr4Vt`kWI+Q=qPQY0Zf%Lx=r#VMZ&cM5Xz*;85m#yIj}U4V2rVE7Y2OL+06uO#TKYK_8K&W2EE;sTbYW~JX_xK!34ps68Vk6C3MInFDh#q+a_>{SFE!oCv>pQwgH4Na9>LK9uu0nJ}o4vJs$V%_z$9DE$ObP7H@HUaAb zxyq3Q9Qg<%+DP|LlO}NJ2Vma(9q!9KD_cwTA1CO~P4&hq66(8fVJyxqb8?DMsdNhX$n(bDr_H1dfqahL&lKK$v5 zrDf)8uMHPaIBSy~Kr(gXH!iRjx)!P6%uYz@C@RgYc4s5(eOq^Mp|EH^|Y1Dmo3y6ts}(GHFe0MkG_3PS-<=P zMQ%AMC-=<`Qs+;C~`Ja#nte7KD zGKf8lUTgMXKQkyBPJv{5aXA&>Zfo7G4FcOi6GW+981kmUO=jF&{*_#Pi_qkieKwo_ zZ9i(%&c=C@h3}Y^(&gLOj~x(jnJt<)yjUX1iOiOjO_ElN@0y#LA@RZ+rM`hk4{sc5 z8G(>_m}ap6u?Fcp46_e3qU!DY`eiS{$+&_&(#}%@`cX+Tgu1WIO7V!%7(3vxsNXG& zZX($9*!W<5$W?gm5_G^v1=*~U1Yj`a9|pUp_h<8dyf!P6TYfpaqKrLtdHYHKT?JZZ z4hfC8H>#&c?BlFfry9KPfNU2sw$>Y4au+XO8OO;vTqIT!zWLDc!6kDBc~BC9=`nua z;P5y*dLsTA=zS&#_)Wm7-*xnaE+PB5b37en-bgsmE*3%HCgnt{CI9BqcYv;O)X6NPziFk#zRIaBD;&FY97k?(OE_Z>^=1NN(3*$LCok?pXXfYUk*Ku( zSPba0BcrstXSO)6miVU?aHSK0UGQ7msPGKMwAp|1ipW)+$hJ~fz@TKxFs%Uw)Iy})e zdvnQb#7UN4^MK^s(C@m5`f$ONLRDVZb1eBms`)GIEculCbIIR#o1_ol2>TGAG3s_) z!|Xo6Qq}bnb7e)M&Ru2~<0Ctk27J2P3*mjuG3B_%SeiBfn@@^tg?r7a`{)3VwNdNCUyL#~a*Qm}~5#V)DP% z%FNFt%+OhrF%49#SQBt_Y%z8Z6Ig3c0bqfVBI_JKk2KZ`hnFN zui^P(RZ9(tltNJ@nrtt2ua#R={;UEwEK}X}gEVeW)O3jyz#WqRq)668Ho)<6ikWE@ z^Nz}_1-`6k%dhBN0;iVF9Y?gm4QGPw<0ALavn8+8-B44xm-ehX7N>9JYc;syuU3IA zvV3N!W-=e~>~WoznF6mZ7l)=$+aFUdxl$PM3XAF$we?wQ4f0IB*d+8(y}H+tf5q&) zmm#Lsni!bc&&Mh!9(Mz1k$hiRh1bB;&dIT&<>^O2V!85aGE9{K zmK}q89wgzgSWaeV;qSwtX1MIf?%bDCZumN(+%N|JY|2ad$~^VTLMiv!MX!^DP3wJ+ z1#0*z|35LkYG;Grp7RliJE|tnS3&v9#lf|nnCYNr-aCBtQRaEWguD+ly>mTs>^=8V zET3&=D)&>>`hCHZlW4e)Pd`ZE)-U}5`7#_=9%Pct7?f5!#vr3p=;Isk;I>8q?q|I^ zMEIMaD)wHWHUZE^eAA5GZ!vPC2qFs(R5(gjdN?9MP^7hUIY)A1iB+3q8E=cI?h zs^Er>+lFUmW`)c3VC9S{oxL9ii3?Ih0hi-}+MX}|#{RGIx!Z{_RcF{2T?>U`yGzki zzM9`Zs$A(zW9NFSMCcAwm3z;h$>E~Vx`$FhKrlExsdBx)e-jV8$fl?`f89$02rmdq z(xk!Z4XEvYhaZnhJ#SCYpOn|Ishp0|p&EN|d;U|kh~`*L&`H~Lx$ksm-W>M7A_8M!M8#IUTnd3x4m)vU(Fv&juDfv-mX0b=x` z+p+XrVrkwL>xAqZbNUtzi2<1$+R5J?$L?%)M{sCuJfMd@Ky-cp>1A~dL62MSCg#T< z?&rSn<*a>4rP!k<0!-RZ&IFFrB`S)?MMqn`QJl3~b?oloUw1qB?sch=n%e@uz_L3e zJK%gS`6%FsE@Xuh)NI!MDz@nqm^HSVz2~$RUN2zFv{nVyq#RRbWJUPAl;)C2hbKfn zTl!A6jJ`7M{@)Yx?L2Xa51<0HlU$-HI$9QU^RzUJk-1cR!_x28=uIuJ*0OWNWWX*V zh8<$Fv87n;Y4%J742#Y(^b70tSMkrEKWB|Q&%O7K!(mD~lG^m95#zAvXy*`ZC#NFZ z927OoY1x-1MuZe4Bw!fXMU>IuNys~>?Db*?RXdE*_d=as9W~ERoYg(<<%oZ1I@5zU zc~V*2PpwaC{I;d~uM6M1_kWYw431pqOVP|JzC(nTQEc~>0RjCXk&EFm7RsH3IJMsq z<{7TdyRUr|eS0^3+45#AZVrJI39NoO02GRV35~vKmc#NonURcQEa4M-vA=$8-BhfkqA*MqQtz|b`$ZL31@#k@r&x)O-|IG{h#D@j zU&@vM5#G5&AJy!fzayN5Wi3XNaO zpxRy7NvsscZ6GRr1G8>5u-Xc322S3Km_F`;5_Iy_)z$G$WlsVlvaG4cc zE+fteV=lF+md~z=DT*6vXqdryKMN_A+%6rEn0zf0$$=CN4u`u*6R1SuEeKjqmu|I$*S6KfhV0LeA6LJnIR;_H3ihJ?oMX4()y@-l4;1Y>bY7cA zar1;LV2fnY%t3&WXpWU6P{=Cr7b;eAae~8(QRwjZQ7h*;F>-IMr5Y^O&@e+by~BxN zXsujE-oQ-)=Ne09>SN-tpHc8{8b_`!g>2gVl*p-k{rzuN#E|7z9aIAcm2o;^isXW|P%znjxhE_YfN>(=yQ}srrc8xUN$4vf+ z9J47wc`9EAv#?NL5>)4KkGMu@mM}zlaxh}tc57F__QK-@zA%@(UGD97Z{Eek9rKjk zx#{N5(P$L1Y)l^K1Oa9!nXdf$g!=+Dr}^I!Na(~)Vo54M-7ETxolEsdu}eio(suz| z25qJU+Nd3{^i%AzM<@cO2cEwU@A^Z07NQM;tGKy&3i!*lpQo_5pjs1zr-P|rB@i%C z6dx>Ch$?fRrDRUYYDnE|)Ya5XvlFt0Ba)tJxw88~G#s9)n{Ot?rgSA#zOBR-;ogqU z@T<;#S)J=1`!DS9M)iFg6UErm30LOgKJC=JH-G8UqCdW2($cttb+SypZt!X5+deN_ zg023>DgICAE#Q_ucjVb%$jJ~x=~77F0p7U+pG#3O05=!;)BPnWbCN-SAJ^{+u=9E* zO&R;73nryYMF#6f0$`bI>nv8Vw*rSO;3$z1nS+4*&%bQV6bcsvbCBJ-(-iT7TtKH` zFCIcVE?^7sc+vm%lY9D^PEGn!!PKon63TY}G;j2n?;aXuls_r@^SR<}lS1DU0U7l{ z(bQ*&!XZa<^~vO_8$VWusOfuHh<_bkm}si|{=kHZhM|@1COWcbU4f#s0y>EhwXK^) z=bq!{B$id)p?%nu8b*k71cE6A+;ISqX+pOww7K}sh~O78jPZF|eA|X%1WF^O*zrp% zU)IYmTq+P|E%{ePkq2%c-=`PZj;MR25OkmQ$@&jTMW#LK5MGx)TCI zL}fa7$Rr$rjtcxyZl}x5+7YV87TgYE1cX|p!)!{l;Y0P}+in*bdLQ3|4VD?TePH%) zFWQNcZXFqWs;u;oqx_risvgMiudz*Nv|Yy54}S9N$l&wJ9G4#~(kGSUtRFG%1VSz> z1UGb@(B*)Fo5mG=Zf#^sl9`@P+%X3`mlv=%I|!HRX(aRR67*1u1NKD?@HX|( z0dItbh4sjMmPk-9nL8yHI3+;dIJkNrKth2**o}+>CZfd^A^%cb$(bSS45o{&aaD+ZLXZ+cWRN3> z#RyZI?Yd|PSN%a2qG~i-i&90PukO^LJtb~Go1DM->|mc?-~I0?3*88^I)7Tq61dv5 zvepeZr4vfIig58XKfcYr?}qY)#xg{dmX;R0zB-2I#9Sapuq0UlS2d+)xaVL)C)wHA zxVWnob$$8y`2qc;v^9l1fPerUkT$^`u(2R%q3@V> zyEa0!iFm1}&?sIXM@J^58(^OI7b&L~+K$>xO%F#FY%J!^CU(fWxSNso))Ny9^~}~^ z$PO7yZLX+3nW)UwCvCh+)bK29Sm0k?1JNYP65_>;h2l!kyXIdOMX77!L|Bt(NdWFS z7zBYvt(XrjOE{<`lvF(6elgPCEDadO9&auQ#uq=w*9Z0$NxXeiYpMf#HGi6BZkrXH$WG& z_rLQ_tX`#EwA_>q?=dn6!YfwtfIri97(IUSUce7E#he`h-}N3A+;~}pzliKxpl3;E z5vqkR8KvzmZjN=8;xJ8~3zm2>>2t8j$-DNfRB3z=A7`neV&!j!kN3_?4>{ijS)xixS+Ez zfq{k9dtm8GXsVrvNj34RGA3)G|5K-+>Fab3&x@~6$O-V+dxv`2Yeo`% znERpEfXwMA`106j1ICd75eVGrnd^(^J}PtXrR1YK6NLRXz6RRE-Y{Wo!2keB)S35Vjd8BLY>{J2iXfP zPvMI4-hx>|Q%|8)gp$Zl=Ht|Cg8^C4Zt!Jv|8@C{c7%S%<&1Mq2Fw^H0UwGcCyz^0 zC^SGbu^oUtm|RTgF!G&Tv;GrRROP z^xW)!0q(?3fD3$Rs@`ShHhh*}k2XSkgYn7`@0g*G3c6?K!@T@At?BbTMUqo+b5P~D z{z&izQ0+Q;I^5j3bBq+1M2ylYysy59yfqpwAlD0m(9p&|HZ^!MF2&<0dI4nBBb_Hw zbfcBjyx?L8DU<#Eax8i;7>v>kgsTYh>ciH-{>(BYUv?QJRLe+iMS7hpDfUj2ayTyLaPt>{*RU ze&O{vyGiopc7P{@792JdU0NCTg_lz=mi+$uJGFjDu~0ur+dk$+%;yJUdnSITu6J)v z4#znuGgG_gkR6?AJ~w#WP4HO}_^ z*pUUWBH4}-%twQUgaW6IY%Nv#(x0I7>1MXTM(7Dx9=|Dke$>HT%!l3XW9F;oWpf01 z7%yR4{&E3En!b2pb4L}hIe)@GzNGz;^Rl60Hq9}-CXOqJ?UNI~dl2Z~YN4i+o~hp5 zS{C#+3DSP2P8_fV@hq6IjK;-i8n|3T0+p3FZwe%6dAlhKln&9>fXXYDxpam?7b1miX>oC8;A>mVhdW`f=!O& z9dgiXRtE$T8XgBpTkXQeIW#hyHt8(WbM82j5x zxJzQxjrWf!LHO=4PsrH(^1Zd^o6y)`-Kod_54y{&WdPZ|>pd^M(IvNo{`E1N=K*=H`P?zeiw+2JJ)^lkw#_5N>uQ~v`jZR;yKi?b~f~3GfS8uiG(8B8?-J`%u(?Uu0 z)*!kOC3t>0VtJQ)C3LDz)Rq8a2N@sJ)aE6COEt)#7EPE6$e{+jwi$_COW6ihowcCG z)T%_{L79s9teE`a->JQL$CeN8vFyaV`NPaj&2=HemnP4;v`upLEXTyRopuWtxpw9- zzXoB`oN9aTwDkQ=+S;^krpaFU+`Cp_=WgQz5sx-PIOJF+hcaMAa(?qnO%HyhIjiGr z1aa;0ES5dXY z(OCS)qyqg{O`wSggj4%ng^4;&(DjuEyBxuA;xP&e3S`>HcmJ2skiO?4VGto8(ie9z zzlH|466i3MO~gPK8_XWM=IhCh^!%clvhr6E9F$IoMg#3(yR!K@(k5Xu+7kqUU4jy(L%IzMM=OSF!@I7Ow7XR_SOBqcO$9on) zX+U_p%TWpD&^_m(2TxeshDs?C0*B}mscEYF9^!F$(3iiGjbY9N%pCdm&E9X|cFfVr zl>$%UHVVPMc*rA@ddR~0cvTOhKPV?NeO(QwP_Wmw_M8WGkao-w$a^3I`NKO4=+dP-SvY3JxJEThLQJfek2!U=1~`JJuI>voEDh?*jgC| zDJcqezxV+-2X!cEm-5+Ux9JiY&>oHwam)nfG1k3W!hv1}Mg`h(YZ!4rgS-PsQnmT; z@GxR(gf)*{d`-c`14bG;K*_lng6w8tl$0HWF=P!mJ3BglfJkaLxDW>nJ5KegBN@&R zQp_eNKd$ob!OeUfr&#tNK^L17IcG~1C_3Ya_}`RpGiEo9{VidbHnu@JZ=t>6Jq8y3 z>lD}xp>o9%cj6QTW?t_6>$0_z*)hBJcjVQF%`Wj`yDuL|PD`@_9X07Zo3h{1>!4A> z6LS0pP)h_Wmji&zG%8X*^!o>Bf%3KNlgxVyC5>YPPfZmwAvIMR$V}>BrsKHsGtCO} zZfRS^`sU_tWZejW-$X&XQaf+}Ejd@?T*?iQKVw~EV}P8vchx+kVCOEjH&n{N;MpDM zsOx}Sd2pE)8dyMHtbj4l*1N;4{xU4gl#>#(@uUVfsdTjS)>j8P2i@qQ$LdAPtO&Km z$%eR#`2y~C9xLlVsVQGjr^UFv2>?x?+yT(PGizA_ABRQfTu%F;IpGSSGQ|?7!Fp&^ z9GerCzF%h9x-3{HY@DE)x6*6#=mY^K;5xxEv0E4x!E0m8>kmkk(O4Wm3Bf^A-!Tjv zK8=`dL5N)B`_nUsu6GbZAL2N5LRfg!Xkl&L5p7$M0j;477<3YuDrmn4={0in%*@Uf zi+>L7c%bf;!*3~tdE`zQqD90@Xo0H2q(MWeLK|Xd0rjEg9+`L5%ZP|H#KEcuI(VFP zAbrNa*t-`5SW}@XnS8#IZ*g}hbmXq2#X3a+$#ltf?Waqf@Y-I}u?^Lb8mHKk$M%M0 zTGuxs9Oc^UKUA}hB7K&=SuFx4+VjUp!-xKck2<)8O$b|X0{yrJqlm+8e8zL=nU^7-v$ zcky-LtaiX@vSdE50gO+e&92J>5i%>d4Ep`ZpAiH~FNBaYmB7HG6%2y1pYvO9%-h5R z9c~;9DUeMoP*9vwT#$?p~x7A-0=3@Km? zlvI-T+BI_n`j43)G$HLk2|zfYC?izJNRxLNR}cTB<3!h|d$(vY6$)?GNW!R_)c90Q z2z8WoP3tK-PLb9GpNGx*`0C?9I?;wPE{PSjBJHvo8mnC*PP3%IR@wUT9(}fh3xx(v zyGtb)m523UCnQvg$J0ziK8E(_a7~{J7R@a=APNXm#XR-7zBe4K%d9D;o3dEfeilPZ zH=_C>bJ$ZLPtnd6=Wfj0JoMWFxCK$-9;IaiU)IV;qyghV@fS9PTQ#pO5H7*>wDMv& zIny$5z@$;z+0ydJ=C{H7Z+|5E7r>yAi>RMhJ-PQ6ge~xa>D-WiA)sC8U&NO$=%gek zpJ)_61in6H$W9fJuJ11Zk0J&3Dorz~H_73BHh}l4ZJ`Gv5s8t`&I@oO60G_|uSOO9)QHhCaEZT;|R`YkeV*?99^`L;r41uW*5RDSDRY!!@G zOo|#}F_EA#4QYHaI<<1zr{K!bj$opAQQMCLy9C3@!D7D`@cbn;`3& z6t_0aCLPfOvgs)k_=|JDPIwQ`hsLd41trcXpr1{#cLA@`Wd(GHKk(9F8<%&9Zl!OB_*B5rY{I~>|t@0~CIsI*NUH`ji8)kOT`po2w2 z9?n@JV`cU@PrcyBB&yi+DBGwV`c32}@kb{Ws19`&qlmB$5~dS~CmlsR_TEStc`3t8 zFwjT@OiF-m5F|SL6!}}5?v^!Rdwo9<@l-*>nW&ysGM1 z<6f|JPLe!={2#h9Ie^q9m9V+}kELl)qjoSDhHi3#nV=!B#p8%2B= zxei1D6<%7k?pmI|;CTr3Xsnj2?ef6d}KN zI*!?E0~a--jKR>_cY%w*3~h?pDNR#7lSjJrlu(XlK@jI75ZPW zk*pv>fi1efZcRhObwqRr&lf-9b&QZ*AbZhZQdD^}k$YB5)Uw+d-=_O%^pmRyP8}# zUn*rtddD}n-_2Cl6Ia^i{Cq6x`){*!{lOA>E1oZ~ROx883no&uiaZEA51`klQcvE9Rpu)MsM_ ziJ~<0&%${r&?l7)Im^k}0eX9CdV1Cbd8uE^?knK-lo_`vnrkb$+b&&UEkyvrxZ{bX zva$^g`#6T41;NgS+~Sw?3uJ#xiI-Sh^voFqhGpl4O&_nS?wp*9BZb>zL>!D2~6>z_aQ3B`pyBrJ{3cl#^V(`RHf%ZICCS zVsuyG#NrE86485sCj~%~1&r2F=ExI!6TFv!Flhjn)0FN2NTDSG7?}z66PR54L$HSt z`V7!5fqFt+QxgXeF)FDqUw!~XPHKjRC))!z8-QDqk@mXH4}{5!A#@DNvBf5X{GSx4 zRVBUlT)86*$xR4{FN4!7o62=3M1F^JAvew@vFr!6M+y}EKOk|-)vtL583&iv8%a=h zc?0#-!53&TLV?uq%rH-X^b%>kgz{8{W>}bmnLGKa>Tvxfoo|gC-!Co3-F{M(IWV@t zLi?|IG}t`EylT6KR*32SHQFmHzZN9q!Z)UHH~Wu_bX6R0r|EI_JY$P%GW!to6!BOB z_$L~NX+~kMdu+;RnSH#x_leXB;yZ%CD-j=ZVY+hDQxROfoG37I)*ym|a>znY595u7 zUmh@edk-jx697hNG97xOPYzUcHML}Tv9nNNIEB64_up>UkuXg@81^KpW8(KFviHyG zk}5>>a?i$!4jh1;{2Wq8`&ix072D2V3$gaG%gyqq(pe3vy#qT9iGYat7eg>?mR8PSx8 z>PJ4ntgn_C$=6<(O`nGe#)*y98BL(vLzP!GF>eWifMDSg3pCYwxY|v?;vf|b=|bem z)n~B*p|M10>E<<%G5NW4rjB9dZCzrfP7l`lk*RA4UAdcRA3HI_!-t>S&}S;>lvNV? z5^v{`6wJB(;PH^_Zw*NqZ_(mBb#8lIpYss&p{d{3X`?zn%@!~cIkE+m4Af0u;g>al z6bd<>1Bcp{@QfcWXwVI6g(}CAu+Q7CcjkQ5n%AUA&{f}kYO4q|ZZP$|M;yg3+y46&5>4 z4R%Tl03y?=n!R+j7qd;fGXQL9a-v`^fz2%jGCpW_3ajZyfM|hAvr^zYTWjuWIRGlP zmOvryeRJy(^ia}KY%$1L52}Rmfh;CN)) zJu(lJgM)!t{tv?5tlqHwgSJL zPg6`W7iy#1bf#7s!nz}{ggP%fSahPuP1u3aKWBtFZSpG=SpOEJk`2F!jOc?s%`H`h z;nBf?rqL?S!O#Jz3MCS~vB#Fsi7-!iAU&E=Oq&|6kI&O5X?zX|52~l3BF=&ND3Vo5RNXFyB3$l^Li(M8_yj$PGZ#vjeIxekdVee3i*;ji$dNbul)C z?YF0ob(VI{czJUb_I1u!%iG2kZtZWU&j`$@NATXdo zVVG{-ImA=Hc+xX3kF|1&{iC0NC9qD9%sLgle9mYo@m0>nLmTy2!4z+!1}oL*2k#D& z0rS2RYnP$KE}Fo+tNc>xh(mL&ciLGYGeKUxFlnm+={^*PY6b=yL7O%}vw-^oW|dJ9 zk7jz%Oe6S&bbx`u{=rA~kca2Xk`Af<+qNw`*sjt8GY#^o{=n})tVKpXH2 zCHp!iJ*1BlXY%!jB<`1G)Cqs5NxA*!>2j8Z{ew*N`GDn^P1f(VEfG_rpMz`Em6{Hh z->Z5YN>RAOY>1-XX=nIA?@PRFibq)G(&l(4&s()vD}x|y&y}~jVNT-*G-<*voz$40 zmY}n|9SG>0ungx5i2!o8!*1W~w4nIQ0ad;1-d-J|quvBam@|4V-CCbCm&k|9DJlza z7!a=L%Oi`|?y2;uME@hgD32dMUNqs3;!u9GxGq@Z;b1o3mT(ZGrGj|Ai{)m7YA@MF zad}~13GP}t;^n}kOu9>-GkQ)Eg$GTpO#IATbhZm?{r={l$V%I9pMOzb;kkqMmr59QKa22Tld$V|!km*-^Ml~X`Njj+(+bc{H0g^xI0;R= z=I)D%fL%?Y{g8A&Bw6w|ZWQaI!RWyxI9Sku2?vw(1QZoOu`>f`wS8_dE*g8UO>S#C z4KNE-r2wKK)8~efO#FRcwWDaUAMp1g{SgVZDR<@I>Mw&aqYKw`Y+xjd&}5O7eUtB) zz~837h3DVsp2?B@Mnr;(mf)-4UP3Y=*K9Mp994ckJ=ZMuy|mfwB8H@_n2Xr%cc-S| zChCMs^AF&5KVF&iaRye0!{!Ek%L2&#${}YM@5xV_l81K?+5;0%pcv6@5+s%io&`Tp z{4kBsPlYS1+S&}{r|*GW+BjrYEf7&q(*tErby5r%W?n-}^1>-c-(k6|8j-|c9rfwA z5?jrhYWM2J+I`1MDD9J1GHDlu(T6~vVt8kSJ}Td+q@1p|Y##^95yBG@!=RxkcY!19 zDxHEswALol^3pd7r+NCFC?wm8i(kh&_1-}!TEXqwBVtQmAEB%G^x@TFT9~y}g$e+V zuiS8*pYA6*F~Bc5acIwfs0#WKI&Lp^#!t_~HQiY226UEgOD!qpqRN3;R z*~W%b`)AvWbh1B{XLl85KRovAt+K-L!*6U-jE*06{_jK7lgZHAtdUcTzBy`-8^XWUY-1?~m#^PTy`o~V@vq~=VXC+%{XO+QBoGL1W2_6oIh z+7LPD*UQRMtX11H7Rf;Jve8oUwaWuLy6>UfmI`%DN-1DCrP!(5JTw&TL zJt5&ToUXdhpTRIA3I;vHqM|Zk&MY)!3p(?bWi|^`x7?uYR4!_mJWEdcNW3$=FD>T% zZyJ$&>eyou6~XfJ)0PgDWPcgvXH0gMwMt*#e0<%W!D51L-B+$`I@a>+sug`(Rg^$4Wk%F;zw5&hw%ouWSgT0 zX0VMXITch>A5QChbd&k;Ek{-A9ZL>B*C^v_k^en_--i7P`iH!qzO@=xESKPUO@ieS zWhk*(e6)|u!(R3%;m?4Cid3Csz6tuj!Ot!2tb>jdUJ;xcX+0_jRR|N8Nl9#m=yW;r zjt5?-usx>gjwQBrO9n|hV*9bap8c@>)vNpny=4^GgIJ!|KV1%Ylan-N64!}uZ~KLHzQ)_E zdvbZ|ZVxwmH?i_>stUI^_UkR^QO<*Im-fxI^3(s{2(d zhJNAGqZh%*6RxibW*lK_}jW4&BGlnhtOwHuZQ%N4ksuLNF$omq~c<`?B zg_gw9{>qDGIps68K8w$#pE*Zx?kmdhO~b#B=i7dbcdd^=41DVsI0v}Uy z+||o!y)hdk_7hkRdItOK$`n2Py8lo(3zc8w(90N)*F8Mjf8YC6Pi*SsM8llI8 z(KuM!`j*$>1$x?qu*|>%Bc@ zHQR53IbfvT#Z&q6Q@FL$i%0I$NB9}F?!fOjw)HCp@dB~7c&iI%w-j9d?+$yXe-n<< z@7@-(3I6L;GR!Am?9D71_LO`7cKGQv<53|N^BcUEcBmOfCkXiwysphWm#R;VFN)*i zD{J3)7M%_y?=54&ip*q~{*{aQrN0wM=wFp5H2%zarZ6ZgA=;)kAEiSp;iurErG?F? z8+XI~{S26oDWm$VGId6%2SV#mc={Q)PTp zgWOGofBT6JQPPy>gf<47J3W6C#crsgFqG!=C+~QGK=O?&)iOfwy@8t(y#K!Ry{|_- z(BCt9{?f%O2C%f~xYd{E%GQbK`$eWj{d)yx0hx+6*M)_f3&0Q zdsckgR<6N3dg6blxzWZ6UfMgU78TWyb3Vz66u?FS*50ns`~=@{-32S3LWteLF|fIO(d4<&>r02mCws*F(GJ*!k5f zP8-g7<|oO9j4z00CTCq_@rRqMsABYBLCTW6vG_y=_5$}-2@Yrf*w*dxPBN!#_8B0E z8!r$f!onk__XtKf$?w{#Gztmz{biU0)T8rOcP< zEmTSU+wG~jTCsI!->Rq+N23|8g$&P4st!G<T7GG#2+d5oof^0ALd>4 zDC+5qH)3M)@1*s;y}chK$38mOp+CMenzo!1C=iB-`qPTT(jz|Grun0N$wevw8GM`T z+ULLV)ca>hsiLxR^$FNOYLzqgq$G_zamC3ab-j&c%f{EDQv=$unj#b_Lof92xZ}_>Lt4}9=O+(ku-&^(G9-1eDwZ^&8 z3|B&iZH41~W{+dXZ*kCKm7~5SFFDt^ciki!8Eui(0G)E!Nl*@Sw6!pM`F9tr4)L~_ zDZI%3TJ_P8l1!~~(SB&gKlseleHQ1q|LxO{va%x5=UhI)CG@#)^A72{BD~DJSnWH$ zR)vKk_%cmpAe0Vt ztTJqn_$-Qk_r3aXSLe)X{x|1O>?~vC^0JKo=jn5M+rwZJqS(!@pXFW2lbT=j;cy7o z`>$7n6&9w#u043w#he7OU8^$AWG<3`6$4y+}XaaEAY9 z`e(zhhX?Xy4~<=3)+7cpt{6=>`Gu*#pY;D?p4Z<3 z9JNT}smW;_NS`wQ>dwDl^ig5lvxxpq4cbY^ei1s9i$u{nsgR}lDK(H_9kE0iizP6QB51^Lh949(n6qoi|qv>UztG;IOcVL zzAB<(0RbT|Mk%d_$Nm@(dPA>wzg`zTJRCAo3BIyu`8M;>@|7<-=4plgOF-}KizMAl zdUgZ<@)+NxXSDKh>Xrj6dA;pPBIX?QG-+3Z{T$;P2Peat+d17%gPzUyqF+E8&nOY3 zZTdYH`*lFGCo@xLFbforB_`s4t1t-k0++IgscM)E754-NfHcHvm1Eu0&;DIW$_bG+6S zockRkz*L1Jr<-W04~vZcNqvV#{^0e;-iOw>3<~)KBAvWM1qwVCKb}$~H<-Ra>^Txb zewJm`{a+M}^*Q7CDx@@)#d%&NKk4jOTjeXuhG7F7r8CV$*8{STHjIjpL`il=A`~f-6&WGP-jwWySuz_$NM-Ml5|!Cd%HC4R zE@ZF&d8?lH_jkNc$8)@o_wGrlC~2+Jr{Lg!PW?;l~f{CLyh_B)!7Fa<|usNfNxPocD5>d&%~iv zABT9rXFFWF0s_{P)f9)0hp1ZR=f;)?AqleSzk2g)e%#RF%B%YI7h{JknN}TO$(HJ20#C*&q|@YA_3gjam-?jPyzSqM9kywVopGob zdS$SQ9V?hFm|}8Kaz@6j{z2gF$%Hv^a&od$WH`geSV!0fU?Tl+%ofhQc>H)EF)snB zWja4I&OhD+3x9%|VCKf*R|bx1N4%w_tqUX`yL#I^O{t!~IJQmM^U)VctLux*rWWGA zvgfKF`E=54xG82{e}iG4X%}pA{6;V=m6x3C|u4B!%!@poL`(h zL5@wd8!kFI$|fHbyc}B{8qQ|nT$15QOIf8clNvYQ%Kq@<$@f8jP6~zNnZ6f4s*Ic> z$z8NZFFv+}J6W*PgZ2(nsCyO_g1CI)&A+ceRB{B*uw^I4!J(E{I9Ap zIUqrvBfjzT=Xf+@4G=M1{GlkAeh`DmYnzl+;5>+Fp6P(B2&;?T2$R7Eh(&NFx8)Nd zq!R`bR{jzcRoC&IBWi{;z2_p`GvKTc8@b`HggK1hDef4y_SEFLn7?nWG5PH*ax8m` z+gSKlFWp&&Z5>ja^eLZbbuFC#-~kk|wa0%tMMP4ks=w?sb>3V!v0qiu+OACJtEFV| z?m)pKuS^`Wch63~jrY28>X3YkTIIhAM0zP@d?^I5oq)kk@KxH&&3z1_UqUAlTy=VW zA@j%Lc!uYZgEg9Rl);xY?p*p_r?pxf96FoYlpUQ=>JOu;38FgG_> zu87+j-kk6|^LPZy)T%GG+dqFLEkxwCxq~_#2?*eQsb|$A-MXyQ)kCnRCtZJkhU@b$ z^XEcB=iPU4j0^rN+iLo>hwdp3@ym|8KH!rGnCn2kxF9*2X18=K!#pc}rdC@F%@_pU zFBWhalzb@u-&1y3EZs`EIdB5*`ysVhX!7BH67;L39i3nV0pO$er{-i|+cPJNrsu>7 z2WEDOL~L0noSGh2Gw15Hm&*8x0Vux zH)0G`M*f2jd2mlXrpi608ens=ZiUr8s+;R)R%AEDw+{ZTVYgc?B?hJ!NEd)3w5kO& z3$UgojfC^v3?*xupWu2J)C0EWC*FC0hd$8c{arfe*fh{#Jrc$4jgWu{!I6l#k3ztz zfzJ6lh{bW|CMG2v!=Td|=oZr9D!kp^8HBvyEDg&+El7aHyZN>mg!jy>Ud7&&&bVK> zBx{A$?QcBVIk!TdOz7nad6<~~)Oz$yj|Ua@zk=24p-=hk)La%pje;MBIS;EEi_R)V zWRU!euHJJ@nm31Ib|O)6x4aW4k`%HaO>mr|;O*c%s~M1+pHANz9PZu5Hi3MrMfZ;BC3$LgN_tCpniR1#E5dJUN^ zkSPqX^Kl0d?#np$ovq%N+A>#zFL~X7LF=m2z7Lr{$EFA(vpTX&Y);r` zB6mxWie4YPtZT(vBd0xSl_&o9;nDyh)TvA`OzYfmx7Fl9R&~=fR%z+r-tnv~>5l1B z;2GWX=`?4-iwo*M4m$Nn8qof^E_%7*T~81VW{VQgS2lNjkLmNwu7g=i-l*d0m@VzyFgO$y1^Nh zeN;L-lk>I&`8h<^gt|X}c&PUMzaZ&FfGn?|w|?IF=(^J*Zfy`5Q7Y7pKu_kA_NJ=J z)R)8STROjuY?L7FLg#f;tq6730tJ;zGsi)D^BFfvn?99IWQp0C8zy^oPHl6jeE zmEO4yACV`EUUBoO^UCUK^(jkFyYMKR)A7I9XH5vND@so-5VU zPM!1C!3NS)-`|C6PnEE}vvj#?ytGxDlwVOteCymDIJf>S{Rq>^egCQ!ilu*%k3(-0 z=OF9pTP%{uruRc{qHO*|z2e>b_mnzn7&gDi+B54_ zGeS0RdEsxV)DlwOuWo*e2^g_N&cnnkqeq`z^>(I6J{)IHQEKQY{oc7?J-cdlV*GH% zM)&jft*%GhDmuF(KDP6?h6)C~`Ljd4f~w64HbTK@gXGQ70$2JcWl7|^t5&Ttv>@z< zLFS4RJza?n>I9=yUR+jsId6Nbo_B0(rbkw`7PDe?$@~8;O<3ds4NcdnHE+vx__|J= zD-Sdr7`(o-l}%}2X4@ZRUji;m%I*=z3@dR?r zd|7r!4f$#xk-#GzagsOs>W^|4GQ7DRZ1pGo;VUNr6Y}ys#?{22kce}mH#)fym5&XY zW`c!D-y96BUpp(Vq=*JGl~6agBiZBUR}=YDvdyZGdQRRc?>Eguz7)^AiJ7;&Kwe|( zrh2B6X~&bM-4_4$A?y8to8{T=?41jf+(&<0g2O)phKxvglHjdx+)e}`P%*wSr+N{4 zSIOEhqP5Ih#5MM?$W@W#>Ggt}75Enm{OJSraAW>G!;euoenO%o+pyhpZe%F@u>%HZ zP|aF6FHHX^P}@q;JBP5XCfm1h)g0C1o?osh_3C+NxW&SB()e9t_ccG?+bX7UzCQ0c4^?Ubkap9-~G2 zBQ4TDfs(!vV za;gI~2{}D9p}Y9g=9jz6hkBOqjLJJzs4*n$c6eGR&lPg}{5Ibds9`3#|A53_0esDO)c)Lo!fyr{N1qkEQ+pma zzC2JPzoW;BH*fU|#orM}ogNq7{ue%aTmHq550aC*FVGXmW`V(DDWr`9B)mGr5?CwF zJMSJry_yX5OLS;{J2c|LcHMGm+Buw-R(g8TNa_on6|SdqbWNr-pYQOcxzSS7Ai&WW ze8?)I%_@G-|N4LF)3bvql4{t?iE)?D#wC=0)zxGB%#B8LIsCY90*-;xV@*&*sh)r`gI$ zJQmZSk{;nvPLdNARq^X7;e`y;?+YTjwC7-&NRVYY}5%nFd|k zSF0jLtjI zNu}}LUR@-r>71+PJhZLyMR)zn;6#>a#~@WA=PI7*-%FZlPCdwL9U|iage{x+l!IARnKW#;YE)MIuqoCd@;cR)o_KWHFr9498Q1iUi^OEznLE#1q zB?Cd>uk0U3&2Ii{K|tYqT!E#At@t7XDY&r77JEXT>@#HeO*4kXCg@W>k@!NAMkKku z^%z3Ceg*l~>LhKL5a z-E(oC6cZR{hWs}iJ0D6KuAMu-`u!lSE--G$?;A!HYYHW=RX<%C?k^&#CSKg{zJEr0 zAo}(nVOSwI^oLL_N_7{)cKDf|Lw9dix(G=QYi4RJINT~7)mXW~{buEejfU4*6`@da zWCt*YaFkZOqpwQ}1VTY!yR|#YIyMW4{xr0-E>&3&kt*7JG6;~0auhdbe?wZ7y@P`= zq|c-#hj{Xo`_0cyO^FbkA2e3N2*eZ`<(5K+CxA(J7VoDg)@ta%p>Y{PfgApAOQ)3} zD)m!FM)8L8?9+Mc$1U0;(Q|WcE>a^DEfr_x4{f1v6arnu1x~(bc<6D@+9^HrWH%)Q z-R%=GX)oLM*&i!*KUMGA-}D!F_ex))eZ6=&(S9K&CT3D;dce%gZ0o^Gna%vRx$6~U z&Ws&$?D}0`H5+_UrE<#V@R?hE*;-RW+~g%WV9zOGd@`lB0~%WfZh6i<&Sfd1#W`4*^xJqKrtKJc zL44rPWE&royI4;AnxzKWjuMZG4g}t(9kWn|nWAalXWSJca?+yUo63Yq0GJ4|+3q8J z{K@Oaq>{7-tu#&2ZG%YOE5`#ELP=O+SV+Qe>)SgQtGx4#J7W_2CI7)h(hMfTOE3|I zqvk#;rO|>*O3yp{Zn?`wNl6@87&Kl{TUKFy{FMEN&40=Q=`y?_r#HLq^rq%~K#koR zf?>&rcOJb_6Cn0s0X+R5Ex?w&-S??Kk&i3~K}Bg;FzMb319=(hoMWHGe%vP= z21naAQHWppHXLSryh=pE2O^Q=^1SbvjcG0`jE|iVWc$<@-%(@IUHbXYxuD4T%LDM+ zFENsWw6i|>x2dzE_wBMtM#6%RsHY4=>MO4ds!CugUmXPYz2=J>;v5W*FP*q*wr}6Q zNd9v^n8i~s$|0L7ASkGztGhkl8Z)5jCUu`Vnl(ZD*dy0QLm|FCus$xlD2X7F!r@XE zrvm9yEMNE*c-~z+MKQH@}IMZffEl#5M+JD*&U@F0P0L8CRi%Ww_t3OkiG-%Fwcs7Ycb_QhQkg*cl zp2uMsA=T|hQ4^BTqsJa7{M+Q+L|3~0?vjETESCFwIPxTLEOz)={1Z)hr7vY;(1C;$ zZfm(>KSQ(l@+e2q{PVr0f{O)LkF|e{AawQ;Z)UF{!&~s1>%1>(U)}d%tM-jfhGnOj<_W9_z6`=aL}92zHFp-XfN zU=)}p#3QS$NPKXc8fMp|Js zG$L>=>T7C9yDW+WC5+w^N76*_E(=W3K$#lw6w$KRZR5s`q%{boK50q`YiPEN)& zEH3y)Xj?CGOGrq3;QqQ3j)LJC{;ue~yubac=Shn!Ek_(_i;f}}wgIF(3I3aQ`=n}P z!&Xg#b;2CSElx)%dKFY%->b-$kE!w*mUTm4K3p`WY}HYujqd2ve2h25-a%65)c5F4{}ZYCB+y$%!{=^eb3^^;lR6Eh%PP>AE=)+XKj1nInX?b_9!OQaF3pm%=Qh(re2 zc(5?(5&-1@gnA&OB%O3v5}s+f_fAih3}j_cq~GQMP_2p9)7Vta6bu(nz|~CVnb2JT zXu#Y_f)7qP(WstMRsH;`ao(eZv>+kNVwr1+6-JOCDpzC~+wR-|AJ(|#v@Tu!J=iz( zq1Qs~`0-xfCm6lihnW$Fx}{rTAx=B-YI!FT028uEgN;9Mr8dIOfxf_ZwJwItJ;U6U z@8g$s(zMYKiMSLI6XQ`f85J3sYybUi@Z^b@gW9M%EY&nA_@dgIk3*_arT3zvZu`P* z`$CIOKx6mToXr{EzNeWvbia?UHxug{hVRlkX~)eZ#Vb}+{mmIB%Kqxhs&K}#sB>M` zINhL;PnZBBy3$dA)X0moo{Re}+lgTc1j@`KFQ9cC4PKU#sSaNKMr>0vx09fkNVd9g}5 zYLL2lboHB(pF7n?Md$0YS4Dj*;E-4E)Mc2R{M$e1_2K;=0EMFW#(xwg&k8G2Y)Rgl z+dMrH9$a_QpJ_KJ?&vB&Z@)`-EU0*tNEz$F*Mn&2#iBnrD{|t2$()%wjCyakzUa&W z9tHkB4vrW>mS=W%==tmU7eNIiTA^w-dS7bUnP*hK9u;`Q1W^3IHdL(nFZ5ZES%A;u;yFc|G6(*Qa)O}9`L z12sgxb*PZ=k#B-V;Iv|d$?V_@vuMRcIw2RmfkK>JzMod?bI<_^pU8m&Zw5L%sx0yk z?YB8QY0))S>Cr zbStA}92|1Ulqs;|*HU42xipcdsHn(UapElcu?yOrqZZI}mp|KXV*k9In3IsHO*s!4 z2^>uXHsfD1yB++D>Jo=38gHcv5DCxQ*40^^@FG4cUqydjff&A_2 z;eGl=Q<;rOTU|h~Aoms6>k3j999#lWE`$T3jY3EP==Y5b8f7%u}w-cT1@7Fg2A?Na}IAAKNt{#d?z?6Dwh9*XiU`PQS~-oT=9+Hi#(30cHU zLJ5~7JXcJrSSH&qJ|I)ckY?>mzClD_;5F9joU4%5VWq9XT^)xs<9b>&@_d%dy==0> zXn_BOnct<>2DcMPJ3o@P92^AFzD9mJVAFQgJ0W9_Z7v z(kqC4*M$J0z%_Vz&OFE``@7~0?0H^&eBu7)>71~y;hgrz+CSVD_WnY3?KHwS zF-(Jk!nhsX)X9kd?A^tr%?6b|5rhn&rRav~-JxwDJEXd88!jRx-rpS)jX^H0V7pi( z#pCszpX-s9OMTZNVVn|W_y<^66x}^I{Vj~Gz*a#OK9gty`MlHmVpwqp$0R2H`m`5) zLM={+iiSiiXqc_&c0&s&86-HSGf-I~l@S>f;qMzbya0X38BL4UMbS?KE}J{E1okLh`t!i0dGD!H>yhko?ucN|e_>XKzIKUJ8YJZ(P-urj#ug=}?t z^%_sh0m-&p9{TBs-o1gTqFlm_2g(lorPFGb{mV^lPe+N>II||t1Z74mg{qG6@=Gii z_lA!QjLt~K%QgTA;5VM*ZJ*=OHVJeDBm3sRAb~d9(grk|mbH}#CZ$qJY0Z{xIw|+U z?i)(i&+b^Nbjs^O0+re+laiLN1aBa((Gl@^5Kz_vN<~yLbtvsDVKtt&i%8a*T6smNW^I~iPpLC}5#A5;mdi!-T|-fau&>l*X#W5)Ao>R^+Yvs07nIDW)u-itg6kRyIP5KJ$rWn`zBaqr(yF*1Z4gxqYGK6J zjQU8lvyk+J^u>WCIFoi5d|F# z8hQE#_PG$&QTgDA97)vx4%JN-PfFJMwk=>=?4@0MEtLs_V^J5e_n!u6jEpB(HW-&1 zQQhD^-makuv_foZ-ot&)Zi;4n66rGYX9Wz4&h%Iqd*GY44TG=96k#%c&;BQ%&2jAK zYiSZNbNLOD{@oW_M*9ppBy*2oa)V4Nvpy0tDEc7WqH6wuHeN$Hnro=Rd4+9~la*MA zF4O$ruk6pR)uCx8jXR(F-7)S2gdq99%Y5M9>U~R37aU*kThqc5dQo~-a;QWY?G~ZW zmogYzRDX@=Q>SxIB(8MgICZU#cYrd2hD|3ENgv)iWUiKxZjP8vPF8$}LL-6=)ix1R zM5Z-o-@SWRd!0c)5yhN7Qh0uGb2;|r;scj{btPV=-YUdp*{P79kBs+&8QKX^%CFZ} zgSdyZM@qgUrO4Nt2Yk}@a&ZyxowD`X985yQzxm|Y5ULGR!y`h&!l()xxroF94Vb?h zbZqj@3k7HWso>^YYj zqgIPQg;7l6=u-QzQqX>%w-oAG9z&&d>|clD%{ibE?I6EpIB4uW^xe}mwh;qoAI zU~wnfG{isTZqJXESN7D9Lp=H?-;e!oUl{j*t6i?RR&FBvD+$ioT)QUOb7Z@?vLPNN zI!69z;+`e!)UVDgYWuQFU9Xl5+(N72t4t51`koNLJ2q%vI3m<$ngCzxOkpskr^Zhx zDG7hm$6=U02nvRb;YEUixMWv70-uAv?039joj+-`@5IEUBOA2ndg<-c_T2OnJM!fN zmUXK1WQKb!X0>1&IiQiT^ds6fC>4CE#9f!iI>6ljY&d5*n%kl#L^D~l(o||p>FCWZoNePc&l>LsRugNg-7U* zyB*3ScA3Qc2cah*aRJx*Cyc7yI4)VxW*j#3;dGb0#PCkK6>ck+yq0iZ{KmM!II`n-x|e)}|XS0WB?z2oBx+ z;}2_uLhf&esN5S=Saj66%-cLfcXn6?xj{5*M5;z4sCAtS69))&4M6*7RSTSxr zXp!|cn2{s7kMeVv4!J$W0yB+s76NgpRb}G8 z-L&j#Gv!{WTHV6&My>JXe}KZ~u^o*t+^soG$tL@zM9HJ(dNj}*>Ck^I8+uORwbBc|e^)Cg{ekZ^i>0j>KH9pv^Mus_K3i|69 zViycYuNRq4HV_{)MFJz_e{bRiZ@4A<0o)u2#u@+_O6G@bo*^#*Q;>=~+t1Y<<4CRE zZd|*PJzWyiVrhdLDni)l(T!V}n}5cxWXi4pz%^y-@L)4Oeal%K|2)%A@qTMChJs9Z zPM#s-A#wW!67feb57>dGLE2Ff_eb=TBF&enh4;x`DbZZ#T_sZN+8^KgZw8Q~^PJgI zK_T}_uYTcV;iAX|Ry=M&$@Df)9!+0Wd(J2VI{~MoVd8@DtP+}j`q3LG!cY{k-P-+I zE&sTp;_D76`)^f#_T3i9)cs9o&j6Vs1FctWpf?3n2CK1J6iJU@?Z`XWjEq&YGe?WZ z@Z)pc5Kr1v63=*eD79;WbT^*$l#KVlE<%~NL0b^*`{#{~Q;exBp{Q_AOxw zvShJ|q_3*vwfj>B3i0g6TcJXmTO-5Jlsd{BR)5G)MxkHiRKqoElKh5-JNEkC2Nmpy zD$!SR5)N*C`K{I)w7)t(FP-pDSiMN(1ShccwJ5RUTdr{)gB5Hog!;3YP9Oo(iW#h% zPK6$VA3>r$ENS>Ht4Uvg0Uu*_1q`kr`B>LF0JJn{sV)!&m~SM0kzoAziCdA}z4EVN z0ew$mmY+G0=yX)fqPv$9;ni_9`$-4zz>nY7XHO6T9>=cf$)oCxx{b6yIR2E0(v?d+ z!hChe?VK>g*>^4_E9=wXT?$&klS_(%c@4JC$?IjBHKRx*^c>MdBqbaZa84uPeJp< zFL7o0!s=;z8L7LR9icBmsB&HY4?kPJ)X0bW>qMI{>Z0bWH?vpC+Ju;kiOh8=7wt-z z^PCu#C{B7dv}0=bu_z(4sNrQ1^a0^Xl+6*6icHI|#Ii4cz0WUkZo3!zak^FCj@}MP zWK~vV9JN_wUvE-W508Bvt8KJR!|VS z(=Q<4@~=zw_5uwVXKx|?eeLvI+*h7s>-pp3gG%ru{FqKxR$2KFw(z+3sMy)rS#+ZA z-Sfxf)vt^5C5snwtV=w~&rX?q^sGfhje{c&Bfrr~BO@aNktn-`IhVnlE=&I1cDIzn9U+B!qlxv;S0cWOo` za4>?0O`2q7x8EI;92q=&@aFQg@u*@^otSB%ZyUJ{xSOxJ#5GC8?R&7P#qaM|;uS;L zZTO6Ibf?z!3JApyrSY}rU2aR&e7U**YrIhT*t&uJ&MWPAV#Y)TbQt{5Xr&DBo-Mus zMWGQITW&EAbi9y&Y4in$3GNG@5i`IDfxVF7Z^NeRa;@N=*rP%|pX z95fqk&DU>{f4|Nue`^aR|0}PuBZdKN5iYV@>a^Qvzd2*Me9@TNg{oV#(H(S8Z~OBD z;*Py4y(1unOU9w{WA&$M@U+X#qAK8|kcp ztH`ZA8p*8O+;2ksZn>bY9s#1=1*fbl&6b_ft;avVf2_iliA{6O$LF>PJNfYbtWZh9 zL^!W7sJ7Y9Z@tr_kf4}#E;wrB_wOvz)X65bet@Ye_pLu=qd8cSy-pX)pwO=S$*2To(-mEdtBb(npvflDklDZc1 zi~2VX_-JzKA_&m5)6>(Mjn{mAA71zv!>fq@_Ygh`*VXP|*9*~!Vw7ytzk(H6`E}%) z&zdRZgXZ55obkqcPgSRI+`o73C&#%(f@0~hPq~CVh*%M*z#vJ{u&w2WMjcF5S9MynZi%?z2jN?lCWp!QSnaES`8HSuj|dn!Pg@&w5?P8`h1>w;izN87Ulo zTdOwp^FUTsmguEltPmewXq;h_^)VQ1%DVD{!V982Dgd|A5qmUNCY?^v+&9=+vV)j^ zvW}BaR^@c#=uTwlrVj1sQ%@b;SBH zd09(e-^?vdcOok8kv+k=$-1M+zpP9SZ7(TxPL!)xEX+@y)h)V2hmD-6Rc7&$f9p+! zW5*5~8Ln%(@2 z6Pjy8-K8!jysPnhR+8tFe`V_{-V>@qC+ow4T^qMZtS-!gZ|`Q9y5sNltb2F3 z9uyIw$0%2M7z7P=2p&4ba^b`MSKw9ILKGCk8>8n@hiGLRufydZtA5*K&qMV+NARt9 z{?!~D9ITw2kL7-NqNAt@kpkA3eX19@l%G2f*$%gfx#2ymrJ~xWaTBM-09C7~Q~&0z z!sZ;dGFS|D`|BticZ!KM_is&74?t~K$c3OX%|4m5;iSf!g^r7>Wp7-{c#wQzLOA!k z#ndy&rR{(}G`6!Ctzl(nj~^X+8?m?Ph3n+iTWYG7CENuN_@yu$s;sP(5~2DEds0#7 z!MpX(?Ck8w4K`?Z^XOHn#kozm7i7)N_tcp~!qw~j%xS{`Jv>SmxDCxo5*iRR10+4> zrtG#$d9<0|#E1e`0fEt1mB)@9(<*io0}0M>U(%JHGpz4bR&X#a@aI(zkKQEtNLoYv z!1xr<_FpKWjgYt042;tRO;1h5;yz*&wPl@|7}AN~FiNvs)C*>@XY^_@;r-h6>ylGb zz%;b-Etz0eeHAo)silaNC_M{H5Wt-;_<8v*BZbOOO0i+HE{z!B)4nv|J1E-o^XILJ zp;i)qaX0$5e_TTW7McdypEv!HAbhl=yB(%}8O~w$l_t@tt-!v%{A&b2Itfk~7f6k3 z^~mAFudbdGxJQZc6EavDa+$AOBeBpk1Ib$^rnOmi6fv!#Vd42Lyo>^RXw&BGT`(ZH zicj|H-MbAX3p0D6ZVOCfVqwvbd-ASVAp1!BS^X2TQQCd$YBzPa8%`bi8QftcA1jcI zMldZDaP`8tP_wlwTU)mQF3g{HSod@?Cxh$Lr%z1&wmauq*4NUtAuqNj$Z4X`%L> z&W|j;JvDU+dCgZ4E=@TXXciV`ZJ?E4laSa}SXii%Qjh=rnw*r^(qe>D^eu9$V$nq` zW~ZhMpjBj)aONRbE7@|GYFK(t@Iq|;A&*8!DJiMCjI%p17Ugi1h*b@|1*?n&d}DTy z{q1joTVA9Qxk*}8<1T-{UY4K1ELG1A9j~MGSu0!=9uwS4na{9cu;ZG@28w~7)!#1I z$^1zumJY_oz`I|?5vdZ}bn?u%kglD>7S_$#t42mfuCvU33p>eY_L(E)2Rb*iRL;RA zZ6At$b1EduA<%V&O#VlTzmXewBZV(1*3g(;Fug81a ztHFEG)}{6AQ@gHkF4yCRa#wQ~*Q{e%+?3wcXWP3?tbw+h%fD<>#8#`Tbq}YGs>EmW zNJ;I`*Vi|yw3Wc82kt;pv?ouVG}*0u)Ho0l65~;Dg8u> zR1T@c&v-eGe8h)tbO8CJ6{!6z>5!L`J0iGE_^DI&1|E}H{S0UN@{y?E2gkOY|C!^h z7JEnAljfo7!;$wpfG@()}}J*$Rv3++jadJSf+EJ!sK(TJRjr!HfU+kfzA zXRo}&tM?ifa63*sorS`J5Bl)!;*M3r;=VLHj#^ssP_rEL`}OPB#tZvD0ATPzto}0G z9=}^b@~u&p0XFPY`|s<~D8H8GmTO5Qi-a>J0px7^!{?P`fn2WvbbTEH39aa4P9Nl* zhE>6A3QBp4O7>}*fuL7$2QU%z!+%2J@^3D1uKq~LB6^F^n+p=~ncNR`ITxm_pC)c^ zs|~a?xw?|?pxIf$CxYB7T66B*d@C%HBj$U8Q&w8qtBL=@AM8tIj&1T(6 zY;0N1utHXHm1@%KO9{<#8?2w8KW}4mE}&X?T3d`)LFT{b6tP_j9)&j%n~?au>d^}; z&%gLn-}eJR+@B%0ZzHCOh^qgRKe>=<9*A!6zmrboY% zqZf0zpWC;8ZTY8fY%-8O=&%6(qn&Nqr!vy}Tq)zmgWrU zs5NTdhjZrx%}sNv68JQ!XxWqulY@V}T-LR;pz0muqKCDvQDv+EyQt{hvovyYS3;$? zd}eNbv*C!6p&zx^oT&hZNn^+MQ)Vaf3o9&z;#g0vDJ=NB%Ky)Erb7BBI_N1d{SMPIW;)a+iM!v9kBsdm%zREU*4Q~l#~Nx*LQ*^=*}Gm zj*aUa#iM1N!r!{uwA@Yz%83c1H5OoUb9WDkh|q{1L%XuQT?c(K{_iWVjIa)WrrKfUR%L$FT%&PP32`+axcd$vy zeIUo=&)50riwY-=pYSZ_G5I8*wV8$QNaB}}Z!L|kTOK=B@l5j_D@sj&_wnt&0vPW{ zW9^^3*q2;~NCrI-D8E)(iR3~c!33~HSy?mxx{psc5}>01c2pi7rJ|75)TH;AA3uGA zS%MzTG0yet)~k~P!TQ#^H@iBXY--AdB0>*ct&4}FF{A)V(ueeqO32315fG=D z!t5e`CFxAw9*~%eUC{B#;x(~GC>jFC7;NHq1JKn3EWUcHFEw-aWj9lcT%a>fVJq2H|*Pd^g6dvSR|Me@Cl}|4#UFhbX@14s9T%P_r1lSCTH^SbO zc0IE8ajQOj%7O9W3cZe+)mpJa?bjMgXOx)7Be%1%*B!a{zs&=aGqQOkkj_EtC5v)> z`HB@^e*UBhdvgtJnl2*L>2%#JakdZ5z}A?_=lwM?!TbAZNr{vXHFE$dYk7G;m_&!{ zl!pkR*efH$Ca$vHTp0GXUZ@4xL_``)qG2KFyR8MoC%xcQ+nZ&Q*wtk~F736{)D`$k zra2$LPV%AAJyAVb=GK~(D-9roTuVbEkHHaX4SZ46xOhzNnVN2s@;Hc#eQlwxey<&B z0Ko4?Xy)u~E6k}*vIl*pd5ig=>1_2P`T`7)UjO?2J9qy<$)j!Stmvz%uSbdPTTgR0 zhIVnFP2&FqLoD0#wMB zDgpF;LRKgsxhx%Gr(41~lBvjT{DOiiz{^=Rxa8g&G6XRphb+-{Vo*E9DLXqmbxppk zn_E%+8-4fLi6geQe8U~?Zm;VeI0JJYRl~wA$lPyPnm7dW$`-A&is`Xb*thCBi=g@juOoZtxIam%(nwvJ zF4!o%;@_vIi`b#R%!+lK(yLizZlL3XNyst78>7BI9yzuKu1XN1_Z)jRJDTI58+`5G z_E2zB&00Z=zrXH0sTlBB_pD8F8o{aWQ;0@N*Po z`DX5Tr?K^bwWY(&ydPn~!3JYbxH>IHa~v1*+wSn_3ul9iTvU!*Ep`b`2O~i zJL6W%`QJzVRLZgOWa5=)0n96W`qMq#>(hPmdH$d}Uj|m-&xIed)do2C-U0>r;*B5?N>-Gtzs4!2D{>+)TS$d(W`W?%oAJ@n^UHVF9 z{c}I5ug3JZh`+yiv;5W;>@Umok+HG8PVUd-@5FiE-1bAf{^6z;O5Wm{c&?7P`yc)T z7~~#(WgflXL^s?n`o288VslF6hEYepZ8vxoW&f=M2g{%JjyN0>6LUT}h_-D+Y+ZS4 z>+`xFdmg|{gDrje`%x`rROf@`*tyl5u5|@{JjNAtaJTE@ z3^Sof)y5~TqXK2~+YhzXnB1WE6ps>!-Nzx&sNZ||-$#}kmhF%8xwDvKR4MHp_$1e* z^)C9I{{Iw)gLmn%Lf&cf&}Z$WEu?bY+QO^*wCf7Tuo3`4)j*b*9#;lFt%Qw?$*cWt ze?O#QdjQ|}?=zkfJ58^DK}eHkrS7Gjl8+>*thWAdTZYV@TLNnf?>Af0ODqp*CGcgn zFt|u3584AyHrdIvmew|(IGJcTGD^E|1BHU*iZ9fJ;o8Evf5c@I64n$u_nzm`G6Gt75UO; zMeEWoj2oT)b|EY7=D(xTwqqN?vlUbX?hSGJqDKVH4NqCALS~@`Nvak;`j8GWycAle zUkHAi3oDET4EsJmlBsmqKp(hjI&%A`iIUD5laO}?sqystD&0F?wFAWbvt-J*V`74U z=Md;b)OCUfZ_*3RDysFPD6_!@oI*UxwQk>vMUgi)PI&3>@0VCKNbuIZ=cKsa)r z4Fe*^f1oCEL&yHQb*R;X6*vGxRG^I#jUDTCOFY_-B)wyJadbn@siHTblLV1mL2*w# zcRNK_T3#0r zWdxfMz#K(Br<{G)%Vl%O-wD>B{NNB(PBV`}JDke4A97<4( zhpOh+uP&Y2a&lWD+6oRGEQ&XH&oDA>M#FvMl*g~9_rHV=Ed_(IoIg*$w8+D^ep2K8 zhs0jFa>X(oc9L5$1LX<;kjnz!GI{gMH{CH@& z2y{fib3ebeyAwDO{o=C$qOSw(T86*rS5_u>XUO&Y+byJ8h3Znqz<>Z|f!-_Dbc6hK z(q0V+RYLu+U6t~*GVqYzmjbn*5E_=#Q*69b#`ZU!vTt1VcYl67ICi627-TK{V3xrg zD(Fn!u+mJS?U&^M&3ti(pg({hBOihkzlp%t-4`OYuK)^+qr90oyZ8M?!;#yi zn|pSeQ%kSMSql-Q{p1dyb_yMhSuo=tY=6#xB=kz%3zsoY0_;jHOsybr1qFc7)nu2t zxgEsvE=PM-l|EU1ju;4-&OyiyvoQ%61SM)iL-z~9!!M98$YQYBFSMe>+jJHOE0#yA zD$B{GKU_Z?Fc^O1X=C{=>T|o!-i?R}?x$4lJbu1t-MaUgvAqwROdhUVRp=5tn|MFw z`o9gY!1E`yK8%K9`i=f&J=ghgy%6**P?fIm!2bRFdCnGZ2SF!~$wy6gZ9#W8fqy8i zy}wL0&*0O~iWHRt*G`-|)CQp5%R3wM0Phzs%MG~uT4djtO5=rh z#10^N<2suy_|S6dF~BGd0259j6E6=^O+ZQ&=ef8*(kYUx!{bOgPQ{No(9iGw`_h#4 z-wutd#7geeU+cfJg3GgNe}b6jUdC9yM{H;5H3uW-Qn!ob7A6ry1a(OTAcK&|$N+#S zSvxgEah;K$3gpTMC29b(@1G?|d~LK8yw~j!7vBnB_?LjO%7bLoR2 zp$Gtrl!MOp#!vcHp|^CJ<>c6d1DL(=Yui3TnM2FS$mL8Kzy1kG8esTqlEGbn44F?j^j*Fnx(_CeH5SMA<&(>2W(jOFW=2>uck^PTr{T> zuuaC^Mlot{qNPx7UrrwHo5LS2h(vvY%LE4}X8`l{0DcTJIssYQAmiVcw6hLSrlN{|q(JPV~NDsn9o2@XQ3(ahEU_S1(+_O^o}Zt>YI!g&9FRSO+-itUM`#FHgm(J{$^GIC!M` z@V8+!*%EZq5*_b@Tvt#~mO=35^(Jh0X>C}i6gj@Sy8uT*iU&Q+#skKd!EVP z>=Fu4Y28~R-j`{cq76m-o8F&a=GG8%ouhHOR%wwshWcUI^Qv>j5wbPLa#Bw&u>Qb% zVulsVUT4bet0y>1(LRDkzWsj8f1`AwOxIEwVDDd5r670oXaMt7zks+;rXt0}0WGVk52dPA56bPBG*^=2ebcjJ}8OTybb{7{B!a(|*` zT4YlQd78EkRPr@2nM-)|$e+2q4=d}9ZSn+sBoA|4gkxK&0mBZZ(3Z3=O8NpsDfbSpK zGwcUGp)Ny8*adB#G>++)FNU2NvjsMt7036#xCUKj$^bBlz522`K1bUqLb?~;>nAw(>(=>jn)HFao1Wy8w^{@c^5r07jCZn)Q_=?uBVk48}rhQuQWDl%a%S^JW96 zW&tU163Y+rJt5NSNGh#NR~V%BB>Uut8*OW$beZPc!pIsnW^(TPJ!Hv1m$E9qc6XEO z_8H%)a>p;PSKVObf1RFp`2`C~{MTrBtVuYwY4>hI+~O;_sjNgibf>5j{HOl-0+8kP zTRDOosT(Ng6t2cEpq$Wr8waI2*{TSl*I&MruKI5O4J}%dlFVSPUgKM{K&l}zZ%HM{ zo-FU3=Uw;0nGuuKrXRl?%%H`l{q-w(dErUDQ-ZXGDX_cvJ>0$xUG{Vm0kvjATj8}r z10#*sQ2s5wKDlqs;8uKzRu5v+K`!14Sz3JPc;MK*`ztQFD{|~~OZSb!msVPqfVGU{ z-0|o6R;3tBXh=tXZF`Wm=UjIE*BQ%w>aUh5S4)4uTSz0qkS$13AW7MB8mlpA4JS^3 z^(gw$oJIphn4OWppLK~3xcR5dle1PxsW-w4Cxf+E$HNuRUoUDcL|QTTdVfIvqe=~sw+(?(SWApF>H~*a zMS;{@{u_s!+&9s6;fJF`92~L|xp8%}N`>{;NrrO{D7j8+Bj?$89DKO=#o-&c@Sx$* z7!;M0i?1;i621I8vDyjj!}r-kB<`s1ncEk-^a?F!Fe;sniHX~p^3EnF8J+Js@k8^h zvMLOuo{UB~VgP~s)UhwL>69E{U*QvI^!;sw%AN?LN|JQ$X~coLj%}(jWs*OOPV2zT zOg=XtibnGI&ufz2BI?FPj$ud1*8>#7X;}#t*dMt| z)pt_!C`XGo>)yF{Z=I*7Cj*K?d|mtl?Xk0Gn}a^N1W90d&=-4?uyr$*XjBBPc?Ld%%ukgzFaT_KhWm8Yi}iSW2U=BFYGC$vd?1 z{uK3Zb||*}e~x~C2gpxwVQ%cChy`t76C_3a=qY;gvo`kdiVnu>x?q`Nkvv{$oJRu& zc&in!1_;L`wrMHR2i~WE9BvB#?H4`+5Nto@%I~8&_69^^qN8`LXoK*u+UoyD)OUbm z-M;;+lu^klA|nbBA$t{WDy4`t5F*)|C?T>cq(~td6|yt3O30`XnS~G`N@f)D|6D!q z@A!8-?|b~7=TP1E_j`@={H)WRwD5+o5~%~16Y&NEfa0SL!m(W3Pf)}C-vbee*<+%K z{xC8wZoKUZq+(MDx=TXk6%rN}sP*_+sR}P|f8~lp`_uM-6?-WT0{hzaz2w4uOMx`E zRnlf*IT;zA9J|5!ZOGjwwux-!MLG=avP7mjX?nSJ+kq#NoTZ z>%s$mk^ruHdwQR$yej9zu%>(@Y>L*y6}5m^rexe_x(opEHM zHSId@_!+P6J?;AS)hA7^e7(I)c2&>SvpEM)qeLboOho;GAP;>OyS&Q|5NQ1a0}F zZ#LR%nbZ5#YH$x;lSzJNjx{~nnZtuKP_X`IJ=xovb)kksUFYCV0!KbVHKl)ba*y4= zeI%QQS<&$d?)1gbv&I&W2&Jj2*mNEgklVMwukDMXWB7IOy^DZ ztVnqZ)x6MAozUCFef@2+C9ew`J}1zMT5xvpr4isDv6-6j8Zld*0UY)26I=n6eh?j) zV#O^WVZjMf$E$a?kZH9_K!!=x@}QIuRgq<$2>x~Y@|v!4Tx$n2`McSHjbq5Nkmqu6 zucJepl9wcS?NHcl8F0uqDq)`h+ib4zfZ^gq zk1C`xE-o%mYDV60S!NP%VT@rtRJErY|J6@{}~yv|dx(hbNL-`kJ3~TAAgY>XK8}49}ZX|@%VTsFd~YEMkv?go;NJ!YH_buKh#<&_35kHp18q6k1mq2 z$f5T-wwl~y4}5ZQCs@6YZ2a%+mKT6oY*9_-$sX2V;*2kmn(IEP>+b zS)5o%uDqTa5S~Pru>MSIb@du@!=r=KcyP|(#U*!wRA900)x61$!nnAiE)`u1xALV# zT$BFoxFI@q*Sf0})q_g1X|lJPk+SW#Zn~;Ew%8?+|K{3idv4a3pO1Dw4ABBe;gzPJ z&J0z!dR_`6ll%#2adMpo)Z)fHXN<@W5f=R)0=ilCCY}9TKgCJ?;ZpCu;9cIF`voqj z(?zDhhBk$ExXMmu{h5=GkDE2zlVF#1*v3j9m{^!aw{c?)YNBvLkE55Xfi~SV?-X^$ zL$vHfY6n&EEv?7??c~66VIxf#GIC7k@NYzvguo50+}Ln3-nRwA=D@@8U{f(adICNf z-b*K_vUKBq;mCIb+SQ-_lurG#MvLUV<#JO7eYMym*H7G9t=jC5q;Dua`A0SCZY4o# z)N9w$`LxmjZ`vUx71cKqKXe}k;qMZFt_RU?1_b7W0Gn!;M(a0vYAAWX$osJh5k1-5l4 z_Y=`I!(I@E29?g>4q4A!1RDV)>fdhr;Sn9;E^b0aR%t`OQ&N%{<{UxTq5N@u&>x>c z&9i_NF$FUW?Qy_nMu#`IZRKD>uHq{lX)5{)G6h@jME*GKrf&FBbG`fKRC{g8V~e~~ zlQ34l>g(eH4$Gp*pc}X|T5GCNHeA~^>v-Tkt2c$Ka5B{PJbQu^(63Fj0n7 z*cT|I+;x%v-x5$x{>OS(KEABAW6MI2iR-t7h~QglTf0`b^Et-;V>hbG3?m?ok`Teg|~@En|CzQ@b)}fxGjnM z^vi4Q)b^8GZ{$2cRrTp5Wr!WG6?l|dmO%=RITUG6Z-EmR3|T$~2|Lm+ZU9rYxNt!| zrR7y!9nH&^FDK{bG;p#Y(d><+{0k+>cL>}H4fZW)b)Y(857VSxw~mjOmug^Oz^Wtj zewys?o40SP0ChUUxR2IKHOqisPL3TY1vuAYs}iH_d-o!MBi{3SiLB%2ZDwX>{yzZ) zIHNU>-{;E>cSil2H>$YAmzI|Y#>dyA&LdzF;+C$|Msna z%{fTK3HDD~5`fMi_)>=%)qH$Z!r|^6y{xUOx(ZDh6FWO(K2B0dVMDil?!ZpIr#k;b zj%R6&gT_4^IS?Ci}%=)*pX=A1@2J>8tJ^ z34(C9YTyzolzV`whu=TkvtI$LbLS=v>Y>l>I=2=hthP4TtZi7Ob#U*4y^po6kBI#` z^-^;@N;&i(J9M+7t*M(Jp}5-Bbqdv8q@T~y)Pc-1g>T1VF{Ah4bzuDrP%?!8X6N6&eHCQUGVdebeFgop1w&@Tpr1;{p*A@)lYi+bZgn$pR$u-u z$T4;s6*o`x%p(_lfSdq0^qq(5nMUU>WsL7+rh^V305!cCXz(;NuAwBJZ5J34OZP@} z?WE77nt$T_7h4_5GoIWF@3~p=>wX5#3?i!`s!>aaBfrxisxyVJ$)M94X}gGbknp?w z{tCp8n-VVTnyi_BxQH901<_N%35!JJi9rJ7>Llu3B_Ki8)N=%v2) z0-nEtwYvVe2znfZLaIVeN1Z-$gcL8Zdo!f82pxPufC;F;{M&Gd@(Jfa?TxU=8~70g zqYRiTAyt+|H${DLSOHWRlD-sRmc1NoyNi|H=*6XIJi5Hepgfm=jGW51+!)TYFC1FNR~C};Mtem> zGP(w}9y6npVE8fMfYUxXB}Kw}!HGBzZ^h!^pk?rwXk5bClLxUE#5aBEi;O3BVAO&p zO7^Vm$Tt@LFZN8O;d~=hg+V7do&f_qJ1qi?pN3&uZfUKl_^aybU*3SwBXVB;p@%v{ z-J{4{*7}5S^k`qjKBNd3edhqpkg-t+=%EzR&X&zLK!}UgH`%xHw=?_6rY_~jA2q-_x-h16M$Oz2I$!TEBzZcr?$|>W`(#X;^!eo|BHlR)Y@++W>l7E^^U~LwT#7r zVfvxU0vtks^aO}{+w%JgLC4v?&!0n$6Y@X55qHu%q23$U!YAp<2BwwQN7g59FrA@| zsq@!0f1vFimwyC<_*pyhiNE6p*l((Pa1SqizbLZcX=>P9`83Vh~4(9Av`8 zQtmRvVFpSBGnlSJa=sp7$IWP7O48S}i6x?y0~*2lC;<|Abj8%^=OfULlmEOh!c*rk zeaZJy7dY`;j0=YPOFi-0ynrLhmMu_po)7K&`3WUzK#=&cW5+5bOSEtmB5o-FtVHxe z3F7#-qI2ykyO`7a{tDihCo+2B*FGIv9DxM>I-rEt?XB{I-&j;ZBrb9OZ4|R?7tT8d zjP|I)U!gV2(E~MMGfJy?1c*LzUN3R~$O2@&PPiSAHo8heLc-%=M(ejpgl3pR{BIyA z`KcPBTAXeK#I_qtCd2c0k7Kjrx8u#mehAW;6S0h8GZbmyi8;47*Ez2Ac z#R@JXfw%Bzyc{_%>5mg8E3KU5i&jf3DJzqbj1)G7!lAhFLde^;`O!z0pYd2S7pTOFu7bBY=X%Ci^pQ!Be%)5So z?;zU(j5*jjio+;ueK@@XO&jg9Aykwvu>1I7z_KV90eVSDkv@gw8B=L=1f<=H`ZFAc z{|Tu*3-!A}Ed*{T&Z|}D=VO6ue;EeU!EuY^eL36xG4G`g%)|{tZ($b2EMv57RnWn z)*$aZu7LG0;Pe35ip-#j*?1$1Q6-YKKoAZAn!g^HVp9_}vFF^Y)~_qBYMT!Xr_YF6 z&BT`mA8V)eJYe84#{D%iSc@)Qje+XcuAy6ti)!5cG$j=r(^S$!t9*2VXsSgDmSi@@ z2;!6wYNo%ad(N7je|h`LNRgh6h_;4o!Pe<%Ghy-l0(*S*RNfNw(G7(|gYgmVl@WAV zYC1ZMDt{Nu^HRFL0!0M1S`FPl6C)$xC0tmrG>K#6t$k}XN)9GAwqV?mv}e1aNWoE; zcKk6tYPugz%9rE(GVky6RU!NS)vIauy{ES)EJ6q{RY}8xKB}Tin%Ga#erokf4Ifdn z_>^n521l^$?DvnYW2Kl1ACoq91Ra2^Y-e_74KZ|;Prnr*ctpE9KotDlb-*-#yw&Nz zp+O>I@p=~U0@ zOi4`i3$yw93V-VQE8o9;qbo6Dz&$I(N}r}Li~@`Ne^z?jtnj{Oz+)=b`(AJ<_VE1W zD9V>r=lurvl%6=aWxb1I55?n)w1r7&(DxfFYKxAN)5jguFU~NyrI*k6o{qUP@RZlx zr_y+IV8M7C+0HBlj(unSC|`a<73u^}5n5R2!W({A*!}g{WuvxB2XugNhUC!%HpSJt z_wzZzO+1-6Id44weCe>C)OmF{#>A155uCc(QSP>%<$>GOS{g&#*Zy=DC*R&Rq@#IF z^@Kd@=g8;*LuKZ%*!INm^mXn>PI&(LeVwaC6gy%2c&+cM8=gN)*l~q)HXAftlcB2b zv>M>5s%5`GpcU|1+d;FzwM|D$PhSO=e7gs08O~e|0HoD8{1cC-gDn~Az(aqE5`1h1 zuhdxVuR{X*%j3tduNV)!#NJ2&gokFHf^`MGuk+$Bd!j`F6vdL|b#7sD;Na1sEBT@& zM&UTNu&X$LWI*b=4piZ-xH#K6CE$Ie20)NkKtx1@PTpZr=zl~EGKJVo4Aoj~3UPAo z1gn4gdn^4!QiGy!BedK|{W@1|d7Iu1!W0%k?_fe*`fqrj7()jj%Tc7wM zmikBg4yrmH#q156RQE5Rp|#TCyt>hbKT*{_&6c9dcc(lA<5^3Yq@}1U#q_}Js7iWa{hfVRiG4a!|nRqjy zsT#Z-LOJR&XYu&a#9o5CZFVFu5Twn%q2kBY!Y^P^t@_bj| ziyVwNRpFyNd+CMo$M5Z+3s6(7f!1mRsCi(x!I0^k1G+`{;>MXh@UBA}odn`dgWH6B zu=fGjiRj009M5;AZ6Nn&?bN%I)PpB6D&Uka-%2Yghnqb6 zxVd1dYs<`Ox5)$1P2vkc;q3Vp^b_4jHR2v<`af^1GeI~?#$ruv?V080yJOz*Tn0OH zg$SP-N0@OUp&H{;P$+F4B&dS8*=q20wsTAH(gMU>gU++CgpG;mW3aCT9PUh;t1To| zGurv!_~Qs$#}W4ANO-wm=d)A0vBdFD;MvS5Ncv`G0syUG=Xzi9>g=5s-nW-Cx7cCd zL#uj$-yA+yKBx#EJX+=aXxV-BR>Aj`Rq1j1*9M#UHiciY$EghnA-79$ zE?V}O(iHW}rRl>PxRtt+6jE=k7d?(6K7qk(G z(%lNw@Qcoaid$hq*Jz`zhtw+=+ivX<$TIzwt7LLUkUhb zArnW)CbVF%Mc&}56C)}TP*Ri7ke4)$1ADuKqFdu=_*~ywEKkeA`x}d8f21Q4mVgyR zX#?p}KYn<#q)jCGigC8YISHYhdb;=0x?r&K_izt4Smrkl$NioeF~>_`(5{PmW5F(N zN`><_5DD0<3YK}NazVrr)MB_$9pRBaDL0-$m@t48(n>TlBpgkxZuge;q+KJd{xC(% z_I+=HwJFG$f=>s3=XC8bV>U)3lAO{mgW>Zew^uF5Ospl z;((B%zmCkRi#@V#tQwk3HI>fWm|DCm?GcgtpB7-%gGdd^IyXjVo%PRt9!@%Mn{?3d z(}%la)^P&%cW0IdQrEa0igmqx=h6Jn(qhYlOWpFw6h9Zrj^i>+=jN;rr3ub8PGMk)befDMDm-&cb z08PPn?IVu9i*YsPxW~xl(YeS@pg;g*s*KcNN6K3iaEHA@!9~AsUxmQ=9el}u{ySes zSME1jT1jz4MMo!PW^TY;@s#Z+<&~qO0!Cw`6c@9whbWntxJ90J4YUg zY~FW1eb*9yKG&rehP$H9PSS1Vvt4%~sZsP*As0>LLl>$}ANdVel4jTI)hlydThM@> zLR1ZB<<5hHN&tle0M40%qLt|sS9`^zKj7e`OT4qvJO?N$6|<1^Jp4N=82Ul(A@G_( zWJnP|mtD3q^@)2|X^T6T6o4RVW-(~c98Dg5btryqqE0&FheDR*;J(07rsbA%`}d@^ zskqd+DU3Cj=_V|5^VUEA79}CZp5S2x6-1%Lrgb@`8|^QJ6~D>Sujt$N-nyPphDhTG zEe&e1dJQUX2bx!+4gMTM;4thb6NLNqx)QLDZJj03XoQJlkjpyp_c zcXvFLcNHMOPR27x<4BeZEVxNELCWcfsWwBswI#xkeAA##0(s&J3Ol-E_ntj4^)cI{HN zw%#Shp&c;zJ~Z~Nsd!IY|1eZLAYdTC*uG=OHAM-9JR2IB0jFpUE~uqrU_g1USoNqFup2Y8ZT z6i7}m=i2|D9y#r!xazC5qm=f0doP4{+uwEKkJbvrys$T8HOh0l0+@cMX>2IpaYU11 zp;&35W<4e!^(3MCV}%bxbcF1g4Wof~Q`RN5D_`VK3|LgjmJNKnm-X}C-t!MWSYD_1 z>;JJzLwit9-fqCQK|S!~h{E%6`J_g%-P&y5KIRJov=RKHfV-2`xcY&YrFH-nrE}|_#KFgP&@9)Rcs(~6UGod&JauTRvGQu~H!!26S)$H+m`aBL8 z>b1Q~TSowyb13Ivn?QD)D|`ST%WiI0sDu>4PC}tkSX^0_ld}hwH2w^ir6v@7pv%Q@ zU<@{YJC8LLP-$v4dk@R6Ls8r@alalGZR(b<^etPh()Llsvz1ZXH=yL3UE*;D3Wknz0ZN0xP z65|GLw`;&uYKJ*aHxDXopbft41J(~8Br1$UU%e;j@I>+Hsw^@zsT>)ZLLwshCAISU z`S51M-#ug!;Lk;)n>)yRw0xG0y5{2k47!UcWW_8sIrt_2Zn~!*%n)6eq^%=tdUNBo z3kK;!h3&O}1Pw;tK_TJB4{;9(83G(Awm0t!bfCc%6~6hN*+(fKp8ay3fItHk9V6nC za7hqBA&F0du6jSjLt|3ya#d(E@G_uFGBkF??BOk(pE66HoC0_Tp;VZ|un_I)4eJ69 zOBb9byb66_a*L%<{>Ke~NgN%2D$Ji=oWZmqOOiD~?&g*h8Y0+-SdKJ3qa9hC-rhqG zER{`PQ2~ZhO|kaOw@_tt8gAH1oSIL%f%r6A{lFW7g|}~Pw{Y~+mM{){B7>14)U<|~ ziikPD20#5^P!l=yy^pFBGJjv(2l_xL)0;~fK8G6{?a%Sp0$u#CwHZ14-;3u+>>MZe zD_-@e+ZT@#0p@q`NDwvwX)~XdL_d3KV##X1R|J3yQMdMe$G3d2;?b`0gNsXORCO>~j#|pp6 z(HI&-X)l3?K(u{G;c%(TdyAJr6vZS75^`0egWz%S?; z7n?9Gv%tPO5cEp-IrW|}%mg6B**1BXp@?n(k1dZ6vu)szq9uSmv|5=C`nRtiPp6>n zaYj3?8BvDB4Q*f9`$rH#pp`NLZ5mijXEa_euy5QkZ3B*_ zEsk!F!@bINyxRA3of*AlEB#s|mo19>>NCsS)aiUSXRqFi^?$x_j^5B33Rici-_Y-0 zzqSsZ|J~)~J(q8?s%3Gvnn+EJGH(nk>mAg(vQ_{j8v1o zY6h)oiFflK6|zs||&lM11u0MgmwW(Lu4Rd09{`@ zo*pIsqtU^BIQh?1ONyKXWg=e5PveqR&T!@@Ww*T%xpWx=pvyc zjJ#3}jj4@{G|beiE``}UzLbeJv5;Y<_u@%u+pd28#Y<%?*J1O_$hKxs%)BW ztFWy)r@j`u;kqh*!%rFw^!EnLg(-yxzH2{UFR<4p>Jh{L0aCvSe)1g;B-6BLBAb-msLjwdCK}z;sXi6+DzSn3s^Z~)O z1Y&@+q95^2Bo(3hqK!?sjnoNoPHl#gyS^4h>e;R0YumO5STyl&%&!a);Vc@c``bEY zKfYZ~e&vcxex!OloBRV>tBA`rK0qVtQcQcJZ9T#v}CyvaS&&R!Drr76JXLU!9MD zzGwnM+j(TD2glqxgdjcDT(yDu@piu3F{YA9rgf^@idW9tg>Ut%ju55%zJH{|{9~A7 zms1JHP~t) zZ;mKQ0a)HCm_V5#j=cS{tgO5z4tj#GKe{Fx>2+OR9lw`lv9PYDX8X>aYeml#+!AVW zglJOC=C+yM>6!Auj@knkJhS;|{ZH6NC@C3TI2*3GuY-|EnkIXOlQ^T&xN!*7zk8Q& z^L#TO8I~4^Qki4}Vxhv{?En1v^Paq(+u4~_+dFliZkW80d!2^L+n=T-yd?)xol7rzP>-$fC#jj=p>wDaT4`H@ESwJInlpu;DIZ z=i)MgHpTclSEOKUAS>ehE9_M&s0v5v=M&CRD2)*)lGsk#zpgpK*^=ygtmkjfa!8ph z9euRcgw^e-N$ZW9+&S6ZPg;*~kzj9Kg^+V}C*G5MT>N z#hr&rdd6aRkYpF4=Z8Fn596Mx?i`BM4kk(m zkcK4O@^+i;O=*^4d~1&7%U6hQ1k}r?_;G{hD(`iKSP>hwCAAM!>jpd@N+`K!c}O4vo5VV zG$aO>4kkt2diXG+xmkyZcX#ZdOq_DiIQi7M-hJteQJ~tldxxAh>Z1Oe`*Tj;V(I$L zV;zGZM&e8}J$h&~D9@S8`aKcT&b-rkBLMKt?ac>!Hcw{v@Pe}F1T;wQM^B)S|N#|ZKu0Qs7XZPINJ)4i{u9~dB z<8eo-P$K7Zw|r7q{WBsKK-)`*ydK@m+xW~t!c9?ihFm{aEgUK0Vxi7C*=>>eyw8Ud zRC+ZuAjA&?qeitjM_BS0BIZ&(tgZ5&ym^$H+dOuu!_Mu;&!2snsi`Goey%CrI}s9N zIQM!Ynnai#(}`BpY#53_gIZre_ifN=n-1zRnOwYJ^&}lB1>N0ze5=71vp&d?K)}Om zpamSt0{r}I(9%7#4G%iCS9L<1J_ljrCVK_I5bm2f5 z5V%VbtB8|_ah>g~=c+yBldFMQ6T>r5<3~@Pd;tYZ0@iR+XJ%i-e*-muCwIv8 z%#8dgJ&t>s@;{LSQNTk@YPb0cc3pN7&An#>Ei|I7A^&!eMb8kRFf7wKLA{I~a;KqpjDW8RT%y#B>mN4;f89&c>)4NK3m|0Iy=ow=RQ1v8-dT~v`KN(8r96!;F$ z8L3whd>pD-EKR=WiS0JThd}5N{OK`@43T;n>FKIaiZDP34!&5X=wn*x5%YQ51&tx~ zZ`&R9f)rl=?lWR(u(WXe6rM$A?lc;?Hm%{=^2TF)ml+k`48X?~BNRH0y0;i}MD0{6 zZvB^`*7!|B>g29yE!{BV>`ZV?G7mFIJcP8K|Hqp>qKcF15tnn#U#}68H(Pd?Yf@k3 zo>ue0rsfEpduB@Mz5-j1^}uPG^hEhu@&c zvjJgS5s)uwMo6%4Ky$P_zhJWR=OocdfzLL0IEeBS%liuk^u9vFK4$;4BpQfe<5o^d z&q-4x?r8o~rl6h|?f76*XrP?EiBqOw%zMZa`{XrDV>+i!S%WuaR5lmcmD*CM}*z*Z*yUwYL4p)5{gATum_)6 zfBAti;EYglqI_?=T!AX0hC|yvh!Zj5H7gUuK@0*MpXZw!=6mef&0SZ=!N>P47Z<`> zGMT)-u`yb4oiyZY1DL^H4R%WiFE{3dvGMW&enY*_uX&UEJ~LK$|CZ$JwtFPvzF*zY zqNq2=EL6cQq=$)P7C$ti3+_Z-|7~6ELvRa&&X(GGl!O_l92~d@| zHdIsUCmLh# zZFhHRlsh{5`qZlZ=g#v<_&D0VYiuN0vy3b(YaZWTJS|n5mUC72qU5bNqC!}>Cb ztHBo!d;U={;SV#F;U9^6=rlH0k-|4~y~jCZH*?wKi3V1>lN|`H(;XQ%)uiI+Yy79b zKM`tgWt$Bpi@iz=>A_cyF*><-iy1AO$)2lYP<{S9LES3CvqbOo5LfdWt@k?jHa>oK zUc87QXnv}eO-UyDlUw10yPLFlM_W}j!k%qQgjE@DA!wZFTyguQVP?9^#usP%MwcC5 z%xxj!2}6FF^$=vz(a{k-N^_y@$&$e~GjVoO;b%N4k-I^XaG@V3leBRK#lAp+dD^{) zcu?`_Av0ltPK?UO_7d!C0WDThyN_XV$ltjfUo)h95rr@T4$)MfvMVRKpTJ&vMkPSl z7sz4i4=|9|1UNNbgUeCkCdhtd3dk#XeD`d{{^cs~(wBd2_tzRjj^wBeTkt=fx5 z@i6ICy;hJ<5v|vQ?wH-D{h_S6(KJGfaf8GGPP8$mBoq|-2`C!qkdw6TJ+g~Ek*_V< z!Hvn|jU_~tif=}8;UTKHhM>9g=XW+e+!q0{B{x0<@O>dFPYgu>hIabr6`0A13a#eb z1w>U45$VK>`LlSSG>FA``kX2I$&+LjM6rL_#=cOzU$mQO-rDx%v> zfeTDV(9N4S751$sj}DuK^GfC+ZEX$A*X3y)chE=Ev=1Hg0+g8!Q?{Nc{{}_`mmfLs zB@bW6dJ-ezRhI+2BDhY8H3s6rJzoYNtgPYTlRt6tB)_6!_m>+d_-so1IJ;4{GeVX$ zIy!1uW?r_l^aFjFNwr`xyYSE2i!u4K?xVfW&aN2XhCv&<{YO^B$9hdMjqg>RetuM; zw~M#rtj}U@A3Ocp#UW4SwI{8T_IZ-9nL0|1zu>t z%sR(mm>{1#yVHpV+Qfg37Q$dbNN`v2C>K;t|7u>%JDI2QRs%yM8U(VE3&AJB0a^8w z?l?3gz;fO93%|0HF-KS31(~kC@Mdx>B20vc(y;*09trK)bKvY*ZenmJp{|*~VeI@Q z>KMVT?1W?KOPu3N>)B#x3bsQ-3sJHfut0+8AS_^;oD)@a^j*Pr@KPM;!=iv?D|i3~2}6f#!r9)y#rYLq$mGInp#{P8L_ICS>%og=sZB?1YC1%Ds>DNs5I$AA4 z--0s#n&om&-B#3<5FN8%G{uz{EEk3>8^2gwn1K<>6o_Xr#(oTitF_sIyHvusu~Qg{ z-J{E40iz7GY{bMm%zB-fWKau^N?zu^C$7QkyQnfiMzJ$3>U(TVbnL%A013h@je#4^(+cG(yq zs5QO^w5K5Y8UX%kApLT6#WJVp?%fm&ut^&a-@3OJP0UZA=C`7w&$s8{UXWmbs1=FT znh|^B8c8{Udff6vZ=cttO9bm^{N?Jy$9H)kRf-~3`+?IQDGmO-lMxSA{)L#XSmIq0 z`>o(Hrr!OG)S#{U@ug69o!`qDx#pXjLcHQd29%deujaRuN-p{)eq+0jnpsNo>(+HS zew5RDsrJvc+scWLoVWWK*I+5*a(Z3loo%m=L^_=Z-J9IRws=L5fIOC;`BaKBo<6-T z9%@U1J|mZDHCe>6KZX2M2z&kUafWHO{hx`&U&CBooLbQjGq;i|D=XD+tBsy2x|gqw z^YhA+-LDIv<%xM{FP9G4Ll6FUa;)&EFNAw!SV4(7qx5B>HuCp>y__&VJIngw3o1Awkl}oZIByBmdh~d zy4D@D4(Yk6mUK%|Ex);qZ5s6*sjN{0{)ocJ0N~aGD3O2=6j*%SDvSs|){56?9 zD=wbDyM5`6%)Qlu)S6E0QyJyA^@lNL%A(zId3aAX|rnWS)kq&B^|J< zXOn+U>Lcm)pe2Rp?N)Yi9cwH9+P$YTMm8m)NU@E?@1S~s7Kg@xp> ze=O(D!LdiiB_PO(&slORXcJF3N=YDwAGUWMMhXCqoaPeO3vXk|v< zH_~L@JsAt2q@-j~@)CzANq>PYH1*I3rl9u&c}4M%{NVQKE%>WQCxj!Eq3j8ig5*WF za3*yK;)sV>JF4K&9#luLJ!Gj@8~|2Gw+|4k%PnNjLEDo4QB0Wki@p%wi0gO(CF#B_ z+sDglaTN`%AF1}k@RG28{px43_H`XiDUn51@<$X@c=$R;);Pz7hTP{Hc>CI?*U0I} zzbo{+YXh{rhkLtOMa4GU+!;=S`(jlb*FlXu+o#z5wxmM*7;wfLwvpFbU_OAEqcVa z0NP=SJf$Lj+Q<|zZlt8xvnS`H>^nwzk(8DYJp4i2%2t^7hunp);XBq}K4vu&(NJW2 z<#5zfDo>hUE(`8AHa-5*U#Yc*+HC=B{R)*FATxXWIDj{(-FGgto1sv`M0rA}&gCS6Bp z_i(O;`Nyo^pK!!wmNr2zWj(dn*4(LrZjv+$7=H!?i3T6gX|i7x&eM-iH62hslOvZf zKKO)WRC|V`p$Le5qC_NRKM*BigjBP%w0tn+sLljDag_W zX-JNf_MgYFSgCNAL4Pko237%BEP<#GGJ8Oj}JmVfK4 zKfur0<^31IE)o|fjObWOf}pL0Wz;A4_tKu`Rl+ib*a4Ao3Us0>le|NF4dFFR3>^^x1H6!td3y=P#cxds(&nm9IL7 z>8;@xI(O8<^ag&ek}co#g=L#x*>Gdzqn8;Uag@P&_OX8%wXWqI!85pzh`;II;tU21 zFO~kJ%n)3P%HsFE84*7r=O7t5iHSji?88GtWR%D8*S609f1j4UCVu}&SK0G{x-8;P zK`xb>DTsNZq{FhB!r7Ai=n*wZ3qg1!D)bgiObO0fyZ0aCPml0K9kgSp*U^=!K0aOv z*E_lhWVL9hsjXhU=4-F_E9M7Bg9(<4iWcufNK_OWyZy+u!5M>ekw9Q+XL_A9vQvwS zw!rRN43Med%nzP@tuTfI-ta>L3U*I0%&{2R(Crq6ONUJ7XffyZ8j4hS?~Z$37k-~G z%{5Rr{1@bD{9A91!($?7o8nK~XoJcK+zr-2I@NC6^cUxX**`7$O1WT1hlS$zilVV8DiKeNn!k`0nmU%^a1b2zeaAne&dF&fk6=7 z%ytEZGZS(QNg_G9hI=`w7M|DNg>;t+RQNWJtc-4tfzdmNc(%QqzbP_*J+u6q?fm@e z$vd(&8ajW_Y>vY`tC0FE4b`5i2yBW&xId+esJb}*5k=Ia*_#JnE$`vK-0CrVdTQO$ zmosl~t8+^3PGlVtF_A~0Qg^BG#py{wH+t@?*q6rUWq zk6P@SKqr^9O;lE7?9MRF)<;1_Nbe+3eaHwWUTky8Zjk*%>P~p2-JNXISL1kDN_fdJ z<=$y*piTQs5M>)q?i75&XT!Jr&ty5cm@Aa9!8ro~B3ggl2_biC8XBN5r;5f2zbYnn zv}jx;T8l6WPeL7w@FjD*XC=W{6J53#GCS%$yiXjV?6?C!g8lH_-I0pxB8MO_AXtM; zK2wPtt~6rfN7f9w1&C@CjjaQX#}^NL?J9h^lm4-+xBueDWrb4P8d6t(=U8En`&j=& zqw;XuZ`jRl0fUC1u@atDJC%L^=?m%`3;j~l*8RnINGd8afB@kqt`;VDpTm*w$8p*E zW;=8TW=*9ydwdh76|jC1B^{{X$(wlVBHc}iqn`q^ZOlJw`?57yw&|CyEY1y`#YDSn z5Oo^bX+^z5x}!gIwt(jFMe!@`Gzq|nyh$=k!1EuRV5i(}29ODQhU6<3@El~+C23>? zc?S}6mA78etRZ3c{4ZN8J@CdG-#<6JV(UrMOBZFJv3)#I@FlfPBau#t%-{|KEIOHbFyYP1jba9vvA$aQ)P_$pdB+WeKyP0Fzm_k zXMZT+&oIzIY4X(?B+I!RNbf|G9*PkbKV9m0aW3PRjR#3O*pW|2EzGKj!pm&LzH@L8 zBi9-BX9*GnxZuP<)j~!~rDeLk-}KJ?F-KYZudw<6wOx*_*A*;sdu>jheXIF4gvP1= zZ;Y;9Ro|Mn@{s%N2H4}Mq7U7x{SD_m-f(;ZX^9(E4=jWztl*HlD-=4@UGB9PSA65h z^J{G0zjb#pMH;j6D24J|>It|r(-)jx>)fi|{%=eEp*<^nkAHb*MygCluaI_5`t3}W zNd7Fiv>`Rh?2+J@1*FdIk%-Kr1&uyakO>7#d0}c#5_G4o@PcfO+m{SqeAP zp#yL`fC+*z|9thsdu*F&=2n#TM}a0cn#l^N;9gM0i0YZAT?e`R4MJI*QCI@v! z{^64Y6)yymX4|}ijr6!A+qQRF-;@wL`0UKsC4qu(?|NNZb<4tgB` zJ2jxUc)<|-`;}}NKv3MH@EV!o1b}DC56DyIUFbw`N`TrYhus?BKtPBDN9vI2#ncUf6T)^20ZB%fl|T z0(Xpaz7Dj~bB`5yya?JERBXC`ySAuftGiHO9B2C+GRGrr4U3(&6N38U%}ZN1tV11r z!zTeq&A(qAQ@6-8yyYjQr5q4LS3?&GXOJwGq%=mcufuV%-^;6{N7o3-Za@5^_4v== z%AG-4e=1c*1UIcLtW3z*H!xuyE=do8m5&?TLzH5;)vws*Ck!vvMaO&=E6i$Qlsxs} z(#e;9R~<<3W+^=X%X+ojMPbK1o`LHsMdTRIZdaY0oV$7G7ndX0Rlaf!*W9FhsTFClq>XwS=pnN zwyrwcBXu?21oCM1jPGT6Q3?XgFC`-JP%|VA#!a)+HSABXwRtNg9(6LY_Gp}W$zCk7 z0*!aN#Unz0a!%7%$SuA|&1BemQ>!<}Bf~MYe`Kv|2*=yw4@S?>)9S=cC(SLG+<e zM9a;XuLM%5O`yTiYnArsj^Xel54l9lo zzag9v4$IpMPXYIbehQa|edW^^5Pc9&3-l=aJ{3h@oZp}PhjsJj0bpwoH=tj%L7{^* zn!qzze+PYEl$ZDa_^}bPAeEzv+i{yN{$Rd}$l=|OKaG<-;7BgFtV zVb!~LhbIooq-Q-1O7Ad@`Q#J%(IdT^b?nym+ien7!p=kK8Vl~r28YHoLb&#p#!IPjXJu%kDlOiJ) zgE4>7vcqTAxUJP_J}XKAu$1=p?Nxz6NnuH!z~QR%>~g9Zs)1z@riRbyif8V~wpiB6 zl^LOEppO2X@#A>+8SEd=!_5GzU)Gk#aeSKny7Zf`6Y)9{2v&nU7=w&{%gD7@NwFi; z9Cv;&o(nYFx9N`|3%<{P*N>>+A2h$eK#XSSX&Tsi{fc64b1dQv*#+*|pG~lQ%gtli>=& z%^zAehzg!QeX66YyDG~-L|tnH)42VSUz>tkfp)#?3rnD>SDxgr^7FOG#%Wuv-n))l zB}=1oSI^}QG{Z}8tWz)5@5y}=g}D|+(kuVQqIu#LnI5-^Jy zo!cS&s?u-z3X)$0dkk2;$$Y0I^yqC;Ztv&r1}iKqmHh-Z2ux-LpwH8gsuw)E^Y_U^c#4)1eCIG>D^Q z*{WwT==ew(2RiwHn_B^BJCvF`nNOrEmhyDnjTYzaK{mrhDIn`30in zzkLw$Hs_e^``%mDxyvMk0QN-Oie~zsxK+K;$jeKfq-n;+a+IXVtwk07UxCMtc63f= zf8=G%E~VYDfnwUfqM|R$5e72I57DrvPj3|#DdB&LNL_>$5Sb3vP+6P5yz%Kl7AO0! zTsri}OuT$$ULU=%x0AzZ?EO2ZiJXV(;&G4ZzP@FpzqZ4JGCNtuC||s(YH`l*7$vPI z{h%e9&@JuFPugv!V73^9tuab0>dx3DSWKcKlG<*2f9?=T0_;a;PK zzK;JpQ)Mx&h}mc`iitiALgR{1W?^k!zT`^_&%*+JyHCB==2%6=*kEbZWUSV5^-l4x zcV7)pdv6?DefGWl4y_t*rL=&;uUm-L)3r`iOL9)HduMKKqw665#5^x_zF}>Jxu5I{y9#Fv@=@$uWGIIRiwQK4-;qhPYTG(1Ow z$B%0NRGs}L?VU?kswkZTF6)|JS`T4)5(MyjPsyEe8M3|cYllE%*;R`E9$Xi&2|(VI zhj8YJ=e)gJQ^Et!%g#avld zVJ}&uy|st(5h;ze`HD~KY5~wqWQmH4uL2-Jqj=e7u3no z33dxrC@B!mE^3qPuMCaDMo_1TqsA9PGf$j89SWK>xu8HFxX;M=_zlz$1Sv#j4aLn( z3ZYa(;TmF^Njey6IU4csF{(R$0RhcfKB<$K%Sxi$Ai7t$VxFli@XwaUb?V&j{fftf zH>lk)nWkIaBxW3FJUE>E(cV!CMEjy~s%!=JL3Jtcx?m`YLr@HzGVU*3P@YcydN2jD8ct+@L1 zHB5Hzf>w2wuo1UIt zjij+6u7%9!#kK{r>{92#e$vyZC0c7=QsX{h8lC#?`c#IkA07?bX2+O~j2>`u`Mby% z`-bKj{bOd)Kl7Bn=o^tnp0w%}CZQc~Qa--c)Rw`(Q4)orzfqM@91*Y2KkAzTRA1=s z`zuh|1hK;J9++`MVzaO$_djN?VD!O5CP^UFp+jWSJ%ombZ*O$Pxk3B*Hctd|A3Q$xPD)|;n9x#G#T(P8Mz?=`yZ`ILj$S~wExRZ1FaW2^QA7zqNQjMM}j_wN=4|`AzBG z-Sc}#Wq7mshlf^q_>Rc;Bp0j{nDUQ!GqC6U(GC78t{PwcDeXU1&$CI+oD+qx}kg$W`u zHGAY@>@BFEye4l|xT|nS`w@37BpMt5&hhUCKlkPZOBdmao}Xgi+0KYdPA}IK9FU>F z*eh4A2z7cKg?%P=j>-B8&k>X7R~6SS{2ttO)y?Mx8Hk{WAzpcI8vJsGmX;A{T9xk< z&8aHyPc_UU%Bt6|U)#OXq`s(s^@@e`2S6-Qcz_S0Z)iw4CH@Gpl$v|@l2mSz%y@f7 z>CVm<4c)Q?amoESI2fM&1BopCTm5HNA3Qy292z5UqNm3|%86gIG&IDB4ZB8A z6WB2IV<{Pt5{o~(=-5I}1zrq}_=x)hxU*5z&tJsglZl~u-HXe1udaZSrY-d?GNkT3 zD;;&s==r)Qr*DXfs1G#cMSuII&=wchc=dU1uG8hj<(3ntg z;vbhKmxmNXs(w5Uv??%@KBlp`DZuMw6)mkY>U8oFP~Q7QCv5`*EeORsp_shqUua~q%u@fJbcShv zs(!<4pFXeD>kpDbSNT@1_hCcZ{A5Y!$MkXiA@0%ZPo#Q8DgERi zE3~Bux->ExA+H5B<>d`m=Sbm;DIIaB7n~$)8jK*c48ub-_bl$fADn`Zf3Mn-H@jxt zPP5fb^2hkZt{SKfusMw-T%31Y2!rq_$*)|&HsmgY^SFK$D=$sdV>*K)kM%a{)B0Rt zt#Izh+?p3d;brQAYp6A-O?dL;_d+U*)}~W56w1*;;LE>1ZS{{ZqxmyE(fR%pS{XS->f z%Qfmz3HUXwF`#*{K#Nryb8PzwM$0oyXSn>Xo68k=PtQX7rXl(H`&N#^Z>mJqvjYK& zIC&W@H5iwX|NC{}vDjrz%$Jp?h3g3)9&5V9~C(Xj%XmJR04uf zcL2K^`20DP{J*ed#AFhFvgg1IRJ{BVbWYdrAGP4n(fI3x@qVFifwCE#ShE>9UDVzs zN;f7}9Uq@>-8XoD_L}gGruq}(9Qy~}-&VhHA-3gHd+m)#=Dq{eHdgRhNyxyrI)SK= zT$0RTQ$vrb-8-}|30QP(9UX@;u78cNQ^83j$`Qv3Z7e#p1iWa$-y_kg`k%EsHfSlK zF0SSCr6=yy^N^WQL^Q*KetrNP2W!T)aP_|A!IFFXnhzbc#Fy^!F-7k)5MfkG+m)s< zq4DiW?Dij{+7@q%bw-U^S(c@kP4hRjZgcHiH&*p@zjOOB(J_0kU%e|-C)E^JI$h!4 z!qI!d{oJj|5ACV42i4+_D$<-i#ICC&*=4k$b?lf59(e3^Ka&fC)RtM8Jw|{F z-|LZ#jta9}Vps((LmCKLC>GM({=HD^Nu@_+_?LK&dCJIRts#0mWfB$srMHY5V{#DB zOG3~>rzrWdv$3`A6?9wSWGiqg%`mGu+gOa4*g12*flLP^_7+TdpwX#|Q68psF1KE; z@6(}eR*=Q@#&f&xkErcl3t|!`jk|dFI`EY{cz6kNS`O@5;VI4CrMYvh(gY(ReZGfX zLYiZut`ISi2ZyKH{6yKOxL0=JVPT7L?!9H^`AMKIQc*_<-T_~N`f&}h%|NU60rP_B zy8{S#C%*Unv3qwCS8c&Nj^QL+D^&PeCB|WbIox;>#Ja0B3Cv^B54qPke$8n^?i2dz zg!t2}=muqcbcyFO#@9P$KE?v{DstrevppzT#9Snk$*zDCzm+=l zhH!q_cq&Cfnv0<(k~bWR?sQbxKx2fVU<<;ZQOWCUg;G_Wb_Wqh#CxX zMY?_r_IZ1G9eNtfuxHyj+^7a-6Jw!FiXAuX7ef2E-kgyaKP9LY4?yPY=~zjR#@nk# zuG)X|x(-9Ty4jv3^;~vgtJ&W+-;#aX+I%aE**L1M9e!{`t^fZ0`!yxkkmd$qDzDpS zN!)Ye#3=@6`XZkb|1%sd$O(yE>_CqB{2LgGqIXe8zdl+zvIVT==>Ju0g~ceYkxETN=886(7e5*<5KBfY}_Br@T&oz z`Vc&L^4oWSoSj~ z503LzRaM!}@vWc%@wgId!(ifo4@+GPmM;~}?;?V1$klNsNn)NGkx#_fqzF%M6QblV zyT)slJ~HoO(Z?P1YRoS<<+Wf-wCz5>Irg6?c_L5lYEqp$r^M%dx9Ic+qmF~k2}iDo zysbEP`l|KmTMpbyk9l;a;?VPVrrVz+&|lzQ}VekoA5+6pX&flFHA zmx3FKsKYq?8!C}Q@NP)!|M%#N4>$C{4X=Z8C~)WLU+(}Krls#_hC+@Jeiwz@3$|Tg zA-TM-3UX+FZg=EJ5PYxGG2C~uOXt#n{W@IF>|foAs6Ux19gqBw6nXa*m}(1|=1J$# zthnm8mZp3A(I?vbM9j+?njfBgJT_P@Cv|_S#JW8@vRy?|D*^qOIwo{tY(l(6Nqqay3z@qBRAXps(1F8?N^RFenIVB#sq{7o z(n$z4y!{}g3h*++OtaGMrT)!mqVBM z>kc;-UJcpOw8PXj*DhW$wfZNj=eORHh>;&cjguQz>}R0gWBjI4V^jPS^#t#+!HwPk zJZ1K}x=NC|3>8O2ZTnwURDK#Y2!{jl&U%p3Q)+wr}4a5-E_3ZAl(D zn4dLPBax3CxBG-%eX)Y3*xy$|V8Kw~=MFhm)7$;agklm-mnqm+{BrkDVqDY7ehzP6 zDJDLijR>=j9zELSI0p0^kSUSO5y7(WoPUfUEx`kb?-BeL_?0Z@Up_i|{WLq_fR`b3 zgUP@XKHuViWNPgvCw-JK77WG8uLKR2B&)&AJK*8Nhjb?#T;T2kD+@aCGXil5W4DmZ zoAiUV$GZzRp}1L@5%WMfG-T8;(`bCX+_KaA*4&t>?N80W&(5pG7Zd2dt)ix7K{rVG zj_;6l@xtp>y35K=wC6D$o9G;!bEg$9G+>BNP7X()9sj1RU%FT0V)5{yB!vZ@fC3$U zPkdTzveZ^?pyUgp_#@Fk){c&13Jw3@Ag4dSa%~OP)*+}C6xPt%N^pQ#efIhD=e6sd z_$@3gTQUsJ8m29Y6JGXdW!=xNyGAN<(XaH#PBNT5;!%&x$t9PQwQ>o$eIJN=9!& zyx}U8q9m=&HqTjt;~oatL?#Ze+9wEWC8-Uj6=IgKsRS$KDcDgF3QRAWnixPi4>D_N zguN?_o6&UMgVzr6ID*knrX%1pYy5FSk)o;W?&dswsv5rab*ek1n5kyD1uAdNrPVcmx!h#%gmG4|`YDDk+09D;8aot2Fivhs z^lB^`GuDT4NSZ5Rh+}OLnhkW_3(APPKXoG~6>(7%C64v#+eM^Evfk9VO06C(Sj(Y}VwzuJ|Bd+ABt5Z3vea zl`v>C)w8y0F_Uo<&J!q{mjD|0>oErspi!m?`g@XbMN|q{cL*dC0gi3p)_26wF&wqT z4Y1hMEf+=lr5IklMnEh?Iw3eUVsp&F(?LZ?4qzekg2yRWuM%Hzz}{hyb=v;qnH%me zVF-5>ILx@`u?&m1Ru-0DSHx)z#uiUj1!t=z+)=Y*n>MjJye=)&Fz7h0{F$cgwvwQ^ z4C}_V*9E7xpY*@pe7P}LHoFTnzh+F9!MO;Tie7&Rfl%Zf+|9XD51rfMC;KuXN*K}N zAWsw6(F&WtcOO0k;7Y)+B4mQ87Sa|l(+rI?JOVr!%+)6>dv`3s1*xzmS`DN{lnCcR z@rC5z()ANva)leUR1P1egoTAQ-?LWj8cC>o-8?XKb&GjUL%!$NkdN!--cOIb;{QC6 z)^C)ks%o`k?yYd<+oG}|5vs8)IB?i;??4)p9##M*NZ7_OCSOY9;^7ep5CDX!cOxUd zCwzj7iex~|0a5EL?jg9aZ2(shhz=@4^`_}l==QK(s$qS1B4377K-nxf`N%GV8MZoN zy@s}{lstTR2si*jt4i>1Q`s|4CY&7NeZcZt z_Z}=vdDJ9{gRKT6Mg<3w+F#JaM}&-tjQ92V{a^rSEOGjmM~gun72k_=F0HMGEe6@o zvG6d4#P?_A4XUHaZ{JRGE{IzZhBx$Oz4^(>gqD_LLLx^{7j_M^9~o>gY3vo#D>jQy zHR|eb4YN=2K9SnYDTDpftbfr_JIXb=~tR|NPUG;p;`m*65HByio6Lw?& z>m@YWZmZu_V%@$cBptLahf#c{sQ+9GmHdA&MZqmr5|DfGD4bGY4G=xVVY?K8B?t{J zp0<-kx)V&82{F&fP9U2Sq;d``dePG6&Ye3$+l^2D7|*O?u>YmJHZ*n@$uh%^HTD{QpH&b#~W_O8Vgg9c=Jy;7QFpb7^N!bJL?36TzCpeRF zz^TCkNKHes1pg@UG{8Sfi8-gU&EI3!FEP|Q+$NEil|E-q+X?;Lv5UU*q*|}9URM5v zh<%RdvS=(Vg?C#P)p|&mI!?`(2nttZM~7qw;kbE^QE$}UsY}^JXrwg%FU&wWx42WA z%I1E)fw4syzBmds#&4M><9 z2wA>v%B%P|6>|%UYIen~f=#S3;zgojpKj}@bmoZ`m95x&MutIYkMp_N*d}T%zZ4hr zPC$FC;bobYzA9Z`gv3kvA=o4>os^ZvVn2WWBo_}}hk!y@JwZvggJJ#q_irjzdiG8n zvV4lG%KA@M-cC4jq9I*Oq-x=y&p*ewPrdbR>L^NoxSQsNiOT**4%`aeXcULeotd9G z3iHE*9`pr<2M&lREXBWqPm91}8<|9shCapbABT1sN0d6r^Z}VuGv>8=ShoJ32{3Ta zLpC-73Tq6CK4F|7>)c)0)THMTCt8p08dJUOo@eIbuRLUFT*gZ&IGHYaR9>|)KJlHU zy5O|fMC99ZLb2+~sfMAEsz(`m_ysu@r;OD0d~y|%+*NsZLITia(IKs8@EEMIr+OO;!`hKRTJ4ST+n@z(O$8H zMmqQ9Ka7vE(sHl4$G_+tTF6Vyk1MV&%IIw{4O>(QDzs+J)G?)o|c@1{#d@Vy(qwP}J%2QD=y^ zfZ^cF2W-3u_>FPjYel?WAfd0SjC%!$u1IdVkZ#M$iXsx=I!TfR0c6kd_Pwy!HweRljQ z$?-dfpAsH<*a+xEHD;nrTwJ1mUU~%f)LGCyNSw+&^whk!!4z0d+}|;}dW=+H^3vNy zeol_0jhu5SG@am&>tOHJ71!D>e{9R)LBG}JorBft_l;G@toMKZAydk&Ur9qFQh!91 zyXE|2vb_Od4*i{6OmL91FPk5=5V}gW+6|l*s16Y*&5CdS<;$05V6f8m%`{=6Uv_Iz zjoqgzs_|sZzxaoP>yKkm9j9l|%gyBiF9T&)j(M048zn8X(pP5CcpTU(`NrUm zr5GuyRP60V$h02?c4#C;5r-$5<_$;&L|@6c$hgU@K`BE@2*`dJmC z|NP00Ka;6$C=$OaWtz_C+AoGJPv&vYn@vopX1jiT*0B6P%qFLUr{J zsEbpS1lau9IM_QAjEfgrK4X1-J@7OZ-gHBlqM-95O4G$Ei)rqBY%B`^i{+p{w-|_K zz?c`OIGck*EV2u8bcBp#*{-h0^o^$R!jT2y)?@tm2WP z{mNc?8Z#ecHAR4lfvtki&PBC^O!wPqFK(J6xxu1DL8P0S^%Zv00jtF-s7g5foS}qd&9&Ra3H0qCzM0L!hXn z_W$!)Kn+Bp2%vId7uu5&0(UEvTHL40aFN|h&EeACv#0pAoh-QSnC_}(ebhfd(SZ~H zKN=buq_iR3YQIqk=3MAj%?j<8`1;OX;M<=PyRu$?GFWNliTnGu=^W?MzvRT2^xoiZ z3UABk?U(v_TTd{v7^|b6bN%_n6u-wITJQ|TalE>D7R{t2u^{7Jnv9Ty@Yq0iBH=yt*fV7LJC+?jpoT21^ z!v?;u8Zb(HcTk|P=pS`7SPx_k{X47mwZj! zoK%gaG!IKktl`U#VRa3T{MXNkV&C=&#Jmzn!-EkaKN9DO!8UMxB<*gTHH(alB+w7x z-s1uW9gG*Jjomiz@t5!GI*{Jnb(=IRCEwwEq>8f}$6Uw&DMKB>WS@V?Z? z-OeINQbV@Z7Pt-n~17ghT$#dS3+d!+y=)lvPxGhLV>uZtTig9%;c8DL>uhE9 zQ)!-W%-0Ui(;9J^<8H`~$8$@^*qdm!P;f8-@kKYF5|h2tHUIJ5oE+p(EGE*x<~OjY zsHmPq*itC3?AtP#xMUm_LoH$iiN+td5dk2e=K*|4`gZ^mLmq2;?bTBCE32!Q18-D= zU=e9%#q zDXEXW9vR+toal_so6tCR@Z9q!A$$ZpL4CH9ueWCT=lr&-=9;NQWg)XrW> z3Pma!Of~`tPXoI5dU}md48(cZAg0+EU4eTd{6&Dekdl@YeHOWI@m5iGc@3R+7=WSF zmxKF~)!i3zE33PVuiv#B+xT;vS+X#{aQEZ+1^r`A-=0sok3YM&6IYzEV{UFPf-c0# z@B=g{te$sRDnybiwQbuGt2_iKuVP?OBlFa(eZt`>y^Fl@c$Ad{7Db%n#JRDAxVRfP zZqRsp`#ba4oH(&|<;s=EJ=Ca1=jG*&1&-Frz8lJfPuQECEv|f@UtCw_^?}g`a8~3O#J&QA|>gCJ5Xp*D|N{0MbI=pb03NiEE z?$OrKaYY%17h2izbKzDHTVGapplp$+q?6@wW0`~Tw!AIMQt3;ale&wVzfBz(w||3j zGL5Sm&a%Yc2o8&aS}GP6#AlIsOprtqNl7N$oZu0jcG`@s4(2XN*u>-rD3AuaM}tQv zomqZslWVSyxvRUos-uG)(+W~)M04JEbE^cT^`Yl(M#zlqrbAVqzq6%oU6H?c_M_H_8r%1{&E52tVRz|qhw%VjO^dt9v4YyO$g?VVT=7T zc~)%C$T7|WD2x{{9EmGKe@|~&=EH`$IF?n2VHQC((EF2RB>+QsB8npvSAZHVt_?Yu zpu>A(N~Y+~gYwN2(-(QRGtgDB)$dk8NszR1ZBFAMtGq~56EaCCi(FhJFwb0=Jc!V< zf8i0bxh8$UPN{5ZVcN1~i;IiPS!~aJK%f&(&B}VD~>$FJH$hteBqXyOLg>v$VKz%nl^5&-R=&!3fbj)>_n_Xv+v4mbkw zH9x;Hj{7WX?@aM@$O{Y!e?KbCGoN;k{_j6Pwe>bCmL0k` zuv&Wdoo9=|Xza#^a6K$UW8ryi;J7NcFfg@0pXlX_lD9*kbz#J;VPq6HBw~9QW-K6Q zK{LvT_-`b#hu{h)Th{K`ix+DE+Xj*zUgQ&nE#CMSoHhf_4;pj6GP0OouGDZm{8+$A zud0=A*DNfZl6|7mK|C`4d{*DqzwznziT=cR_f-Al+M3XyV=GuQLd*8w&a-L{mfiE0 zV?&`dE~bHPBO^Ef#EaD@t5h`5KKNCr+-9j5vGEo`tST~lAy~yR70sDnT&%9CxeCi2 z3;S^lI*YE-BfF1SQf#h-J>tz;m!Auyrg?5!efx|YWB$&+U!Tc%{h9QY?`9&bR5R|g z@h+!vF&~eUJ%3(?J~xT(`t6nl*n%cK{gZM>p02Go*$1{lFqt8uTqwg$Jy@lrka~xn z7M*igq~R89ViemNaF!(}*0x*VLm)5+iFX)qX&NUs54?-tViVI)f3Ypeuc_OzJ9Xro z%)LnuxtZ?&-k*`uOnKXK>y=g3{qIE;!p4JTX_|j2?4RZlQ{}r$KSQdSe?RPtNQd1= z)Z@f4{`(K_SE;42-~`~MY+w+LmjwMViF(+sxc|2h7gPHV0Mw{YCC83M8eeI)8$%(9s7pKtGl4PX2nt;fvG?IR!m@QFK;__KRt z*XNNEqv0J9qg;!Q;)~m%jk>;L+YsFdX4HTj-dfxkpS{+l{&%vwfV4r%8Hh9>Y?1WBa>^iV$nA zmhZmAW?yABmlPo&;bdl8Pge3;A2RRfj`k*#|S(Umf-6JwuGlRy zKNfd+c8he8W9rBgneEN}GPZn*VuDYx{ z+};iRa#m42esg7=d#HUC{I|;3nrl`M9auLs^pEruP2AdtlQ}JxL4sN$O=5vH?O7@_ zp&{al2lJqvk%re4$rObcW*s8fJ}Epv*N?^y)pz++H<%q`iZzXmtKq)DLk8z4`vpWq zG&_Ey%W|FCCIJpG);H0WLloZVc^n}rxO2ypo)T*o(5ib+>5_>vs0oBkP3XeNmOru} z!^jk$05v@*;e;a(Uno$^#}3Ah_Ct0Fp==)>If6E)c=z2g*^HSy6N*3$Ld(Xe)bs~S zOZvA>o_GfcmB7spExHiu?ZsApS&S(tCq%r2mqE%jyo_o~p3uxNEFF~9pd2iR?!Coz z16#bwFAHRj9O+_q>(?D0(@)*~ChzEmcRAKIR)miB=QFfWaMD#J;q;yHiiF&z4 ztA!U@N)y#*wSNrtZ@G2((2p02)6Oq>EnZ6WDh;V@9;t9dzO@`5)-Eeq z=yN^!%aP`6Oo|_ROM@a|EoS0b0V{c@o`)@&+;m4_W&v>~C~*Qb8zdwcLEw1`%;5EF zWvU8(SB%tFy?_6njHp1oxmer*%em26dL&-Rt6$_e2R3sWywcRo6GDc@$e8f1X~Bxc zl)Aoh=R!{u`eU=tfBg9I6woQja+Y>ztFs?rZm}YVa65{*N<=x0qpZTQ%O4|XJp5z< zppZuS_2lH$j*cU<2MZoNSm<;#lE>U`s)hp#9NsHyF$aO)&ojIh14F~5ii(PuXRD^S zz|6Js?OR@4DC6J0orB03 za*3G4jOvUDLkn%OClTgmtZX9&MozmAmh-7?T#?yM_qt2e`nEw;m%(zy2xbO9rHB|idGh|fFezy(4c<61mY!a|W^GcS zn7M#%X>qDi)zw{|neE^DqFPwlx$n99vdfM>SJCy0)^dOAS-^O}XEblU@~FQ|am1=+ zDK0G9L;6XxL$lAQ)XQJMCxKVMa$<9XmWrPfsU~Eg265&;WRsArF}#xR-rN+a(uoNV zq+sy?FHrL-y%OPNbNKN1zXH{v){NEU&tiw+v=A7g3g$h3uIq=;UP3j&Q&?o+US!KvZb=+ z)y#G#%d_EG8|$`acIQ<$KBd$9Ep6Y~&GgkwG0pVG3eivJxs$gEcp96HxW;vuc>Np< z%IwTbHt75px5In$VU3TFx%TEB$At@flz$B<30W$RjL$JRFAwqwJW&!c)?v3nvGZZ4 zz1Rmo_VE_UcLn<@$9MnZb5d7b>v;Ll0V*}0*cZc4^d4NcS=mC5iOdUY#kdSL^OL;< zF;X=wq$JP$_Ilmb6&i_Mpo=quSh^q!{>IIlRB}>-?FJXkdq-dL`N7}l3XGPBp{=Yp zqe1);OpalwR0V<{5MdDo+%n};yz*|#fJi+dR#jk0vVi;+Tu@LjWKV4mQ^{_KouW=? zq8+SjXz+RGOK3uPrAIzcW!_)`5F9LZZR^5Y@RS_dTKeLTpxhuxHOQAP?(QtSwRger zvBnUYpu~hlh^3OAdmH%E;2zMJ8 zkUi()jOUYoC3?%hUVn=DcHVmR=haR_9yN1!Vq??k_CD~~o%bTw>%I3nd#42liH-!# z?cY`SCpOlYE{y&|0piG=@&7fSftr?&Qe?!nV4XYu+q#lWUeFcxeLfp-SgD-$W#zD5 zqKJr8ly75H+I>Tt%GFyBzu(*Vespw;LZksXo&aU~4b8%qYjdwxKLd*p#XTQde|nzn z`WVG+!vKt=UjmjTN!gIb5W7X|DK@bxNFI>#{si)#t~O<=@nx6+5f2T_2Beb*dDdrO zoZy8WL`G-_Af5uY%07Ui?uv{fJBi9`ZEbKiIx8E(uf>W83iMxBq zz|YUmFHS2W>ibc$`bft16vl|tlEN;sGFjTOa(Akp>n(+K^-(4b)O=``WH6*^f{?q_CVj|wt|E%(^4mxn!UN~4hF>=Pd z^WF1L)YQICy0D4Y4+U1AU+`rr+oe^h{844C_m2be-RX_J*Z;pqr&0&BaGh^_#Wda_ zk-pWF#mq0#k)z^EzM5>olHDgy$`W%exV3)3N?M$nY9gTwt!yX&F;iO@*;tf%&?@=< zq(XQQefhGG!Wt&iOStP$`mDl?G{4SHmWI@;z_3ZIC8#7JSZj7)z^WtPTmq36;ZAn{ zoW;RUj%{W97+1ig7=nm%p>5=BNf#oqZt1Em^WeT~nPp;e`Rn_EV!g4Gnxsl6qSV zwK(ZDV&*4(Oq93C@+qGEdNfVn!mc-c#PJE8()+N%cgtk5J3{Ujzq;`%l5Ko=I!^cV z-+~8x2WpJVhl=YPzP<5O-?Tnx$K9&UarKY0u4tksTYqMnAFj97Os~38W0BJl-5V#~ zX271?kH+QzHj_xcwMx5C#z6N~6RBkxB;|)+yD4)s(QA;YE^iK#Mw+mD@K9VisWlLQxQckyA7Z7+Y$T zgZ=7Nt9GlXKxi2)e?Kl*w$g2jhe0NM)( z3s8It5zvLn&Zw2#tqA3YErIW!FMJJ2m*coMX(V_?;UTZuk;_F-6NT?eEG=qY`ZHd^ z%K5hP(9cifYfhUD*#6f*QET`)>9@~!YiQ29#yZJ_TUSOs4BBO#7<;yXAlbxV5&|AI zphc*33I7+K0PVx))G6+VXkU8YynWjSvEqwd7ZLSsa1ir@fa24c%jZKJBGH_X7%M|iP?*1fxJO*>;aV@Q5Y~d>+fvZ7&zGJ4bZwB5o%P(G8}K;u zC={4^9X77GktAI|P@#+N_>k0fw&G;&Yk0@NhB*|geaFvf4F6oExS0}3=YxFvr4B{i zLylV23v#D73n_8t`7dymcNNs*uhP6@FFbH|r zC%KE}5G>K)te{$SW*|636z97q(OKVd#va8q7ZOvVEko0?ei7C zC(FjNZeIiaFb1F{`yx+v;($*XU$SidgEy)2T_BPG`6sMqz?Lnp*&or`z(tyGCt_WI zf$bc>f`^fc0|Al_p*9QLs~8f0&Nn((I6x?#Bbq2EZULFcw0d)C8Tt*(q~ z=btYKXB4qr>wC=V>AT~^BH;e-8u`9YB;J1N)s7vKclX8*KTGGh{z3G(vCC5l9nZ;O zW7u#VMlYY1PD2A)bTjz5BwCSRG8>@dt7_a@aghME2D&8M8z~;Vh?x*W*PnO}juJK? zUIRsE<*gP(FV~!FsVM8k*sf2CPM9;gAc~y)MbH(=(Ud)Ri-GD!;3}s0Mm#PxjZ5)} zzQn-=K4_oA)R7rbLy6@c>bo)Dw@;s*C#UmeG|!AYJoNF}M+#+H_q`hLh_X7|*=2KS z1E(|ZKCyF4cD-l6{BA|q_&Bdqyotk(3RhY`pP(cO-oNMvQ}kn2QMT^9Yf&pyeT;{H zzY?iTco_Ua!(wkvB>F{3h+|3VtKyEp`c#biF!5qfIv?^Z5uv_BZb$~92D-zjy@?AY zvCAQdBcoOf8LV2j-qgBubNI(|6>m)O(@+b67W5Er-BtzeT2I9C9xu%)wsR74??jVM zj!^swK!UbR7Zq0kQ_d#NY%NT-?l_^zR)c*4o~1n+Clb3vTqfYydJcn`Bw1$?@+V~u z{VCWk>KJ;`ukcFk z)#VxgPJvM2#=NxBbH1)>Wf34&p%FLx_Xly1s) zChUIW+#k=ky(B(GpA>##LEz>R&VGDL{?5MJYanzq%C(WhQ~<3ZmpoHT1&|{A_Ye3% z?7V7MaK-_1UJ3eYEw&m8R;I*D_-7D!d=HgXi$P$0LjxIc!u>j|S^xMm9iz8;xM6@i zt>aa_0yKum4v~Z{LxQXn))Z2IRQav$C?1Aj|~zVojz{#Sw?2_1`+p;G_upf z)D)(S;0Y3+au^?Sf@nhmkpDbdB*X#OLpfqH{F{V(!rvQy90~G?MEU z%%#W>mn79-Z>QTeJ%3KcB#hvA=;66MH&djp?wX(ehZt6Cbzn1)5%1{fF-Oc%OUkDZ z-tcy|_Y@u+>4)K^qTXbNDFLyMz^{j0EjMgNy{yTfnBNqa8T@U0e7A{7$3K1lQ)eNc zhq@I7|poh6?7*kiHI&hAFhgL?^dfjxjL^ey7&3X;Ilm zd==pju>07ttdAfw;j4eg;QIbYims=~aGI7jI>Qd&C!z5ZWbbV$8$i_jw$qnDr6I&NFeKH@&xhy= zY}vAAuhL|kL4_P9Qil<6MCMl*ovjQ~G5C}Ox0;n}cq&P=36@g&PJy>*+u)GO_2R_~ z{QWxSO+<4-k-CLK5|>p-lhc9~=G4D_el@odZfA-DL2N@WF?IzP5Dzj*#aeXK;~d**|x2WUqTe`QM+(G#y`5g zwZ_42H9h?^)S9lKH3QSKC`Jne5-fqSNNbHN)Ni6i!^PD#I%mEQJM%qpGS+scFq78= zL~~pB4+kjaiR%QktoQ9(C|#%`D>1P^6hjq;7neZLAlYqb1oPV$9sxfgr?doqgw$1B zK>!Du?VFr1oky_9r_+72AMNe_p~*1x9`o&k)g`gic=+g1qv7mz;#7(jfg?jUdgJ0n zWo(YsPl_m~32uqY1E?m!C}s3e=&cl>{1_Y@T)B7eOE=y}PF+%<=k?WviiZno5kAp3 z+%4d?{k?{$wnmW=+=N@3$@^toF`sc||5tjet5-fKAGR5F+e;ld1`JSBODno87h%%G zKo}RO4fJ96^7CU(M^yY6AFo9DHYQWt_4;*Pc@1u+01!v(4<>{>gMypz*N*o*p#Gn#m zkgkyqNW5?vf;mzN-;0QyO)c&1Y}@Rf?{;tyg_R?EA;qpvh}~JrX9l>tMwm^d7LV5= z4-}*%IR@ZSTKbg?a*q7np`Vz+72LnSQ-iMnn}fjDR6H$|68Eg1651<@vj0988`7QK zUnA$++*QMaHT*l&tR$a3&4$rHhPZi?f;YdTFh9Qvfv(~Bjj^2ThV*zAkQyI|4w1;y zp@hlrTtL!%INBimiqW5&61NeD5B$ZVqG~`%j1VxOjBS)n%zO-1IFH&5Qd4xb6pREj*PXBI!h*g>S>81n)#b9z>1IH+=>_6oSs zWUsWeB~$K*3Ua$X3y$a^w98RSBqrl2HVLU9{N`r{*^=fb&^GO#p0WgBEeRUA+xqLV zvFAZ82CBOZt}oRHvh^5K($J`N>*Pd)9V+@?ultD%;5PyrKIQ6obRy^MQgX7Gc^S}O zD7Luqm*0&y%n*27z{5&HY7)zqTFe*fM45@15Msb>irCu1Qth+pZ zSTTXbA*6fdEGEaKv5-5_8<#N(O2R$&!97szs9yG=+WKK{Z8_dozS+DCEig&s$TYrl z*-7S;1aJWIT#@ENtiZ)(_96od_{$PJz9jKU%Ti!HjSk zu{orYXt^)mfN(Z1<-q^mQub9$G{6{Wql_L)BB9*i&SS=KpraIkhS$%oqafyjz1M=2 zcTSc$bF`nNqNQ2fSlH4Dvpr;cf#eRFmT2cU%ym%*P)oQPX;9^*qwjl-XcQWPd0kQP zbSU{@Tmz2}3HY9*=PK8rCL*}-^P7$+vu~ii&2RUGJcD5Tne79QjxL~ZA^$Rl8jXf8 zv8*w6CHGMVA5&sv4?`B74dMAO4^rVb*DJ|DrwG0j2!b&g*I);OewPS1b(APl*8oqy zBZg7VrC)HcjbmTcE6f=jKQ-E;jIZPZn`YBt6mhuDo_yh&4$*D$TPW7BtwGIv56>8` zYWSy8D)H+?Cnll}-9O3&l*8_UFJp8D2~P&3`ue$vGO_7fXXUq;xah%3Nvj1d3vFc$ zWFz;GDk588T!qC*`qf3A@c-XKg2|8w6eQ+^0Zt>t9O!sJqHpUdac6?j2TqNIk!dUd z(on;|@jyxB0;YQn1QYSJTsV&0mJ)Cqrg5rbl_PtAq-WT@ty@BaVrm+JL>F+L_M^2i z&P{;gg+zLSbP&hS%ao{%dY;$;bqx~-8X)n}IoIx!+7v}>4a^G&=hUR+Ag1^rpjIAz z9r8Q{=i5lm16xPCWK2+?^RoZhHw%6Vi7X(r-TdEIXRu&MDg}w(;)JAaOU4il_42br z67MidA%;ApgCJ8hEYNE7x1w#&e~RE&1#z*O03(o!TYq_dpIq3IxGf2T8qO%NXAD$N zk3OQ5Hf#&Xk$5QJWH|U-YFSNGCpv zefl2%DI_XN11}zUNR!WF(dbQ>1Cz)ocHXCl+19P=&w0>GG((v^<-fn{;7$pRw7!p; z_t{&E|ArWu-Nc^=n$;nW_8N?_tk_728p)_(lx zW7XMHlGZkiumN1%TeLS@=A*bkh`{^d;cVBOsP(%J<3&+!QYe;nb#;u+`>PQ+!1Zy| z7Nsf)R0>hh?14+<5Nohx4$20W=_sfP%yN41@_8A38MWd_LDIz=TxD8 zS>C7fps?^6IzAkFvrRVW-(SQ36!*xocFeUgB zaIIRkey1NJKO0l%R}5jvW~}kdHp(Iyh*fai4m2t~dg25KJ~Wuk9<$JSZn-m*KHB=s zFM-xnKw>1}AdY+Mf((inxCNyit`1A<*eIn=(q!Nyb~7?sSohkF#5?<$qkEHasK8tM zCaBhYyUtt$B|>Ky`~L5Ti(rdb)ESY{gU^%Y_71P|d5`%Qu=LD#-`GTrD$$cw%*-Hd zkXBAkPFbe6!aoXy_x;2m`e2UE)fkr(3m;6+*6~MskZ*uOS+!pQ+F-z2MXN$XG?PEN zm53;^$tx%jb?J&XufJ`Dumc?l6QfF%8A6%Ka>&byQIegB)e7um#Dw7%(en z-n@?2h;B;M^CAzlHTP5Xs91g^`oO90F_Bty{O_%x{i?fdNCX?EZcX zO+n6vQWrRmxM$ps+MXUBTzlaAp06pwxkv*!NKw=&PtUo-pJq|LtU;m0n2F`O16^nAS{1Q^JT^E*%#CQUj6r+cQot+S|Mge;NN#PbXl-b42 z?bq)S!whX}h(`m4^DH09)&@VyEAyLShT%Mw@nce_tk*sC(0hDp-tb5TQx-7K7L`WW2a4^rmn^V+*K+=&8-p z6)+0!k-Vb$a>waktdKjmd%fsD>n&kh#~5v>bpp*fm)v`xtE^iOK0XTttY%gS{mO+l zmQ9xu6GL%zhME;X`e31aL6Zu~v6CH37qB1_@!e`(0Wc)Fi;9A48loo5f?AQNAXGxC&V%5wc|yffa~5ojlz;7 z9ZAT@RAU1!yM7d9BwcdXS^uP_gXMlRGo4s|0VB1~o`rn(VVrYB9nRHh-}6usz9pMf zxaRJo$4%Mps$!jfR|B}LZ2_pW;}zz$Qi=4)woxx~XdnO9 zuTeAt0|;VuLR5~DyiCW)9D?g*SGM_sEx-!|4JmU(s|z$qGo81%|HB6tWE1b099t5t z57C+7JN#*{$>I1+ncI5FHM|lbR6&nVO3YgX1(#)L%Ri|Rs%S$MT7K!;wR3fK2@NY_ zvKUHuD}2t&_Ntlyn%JHt*wSV!%iw^GrNCN28-? z2k~Xo2^D`i2wp-CO2DN_nLj>_lMCheKalyUVrz+S-fVF9fMnceb9TJXbu!%1{64>@ zC6RHv$@Ah42+y`3Fw?#o5Hh6m`Lm<{1FJ&!PxG-EH*04o8M3XUbhy< zTQu|Mg!fM`*uYcB>(@jUUWex12zdW))Q-EP5Ntwb5LgZ!_O%pBgSpSd&^hc9ZC+Ke z74#fuaqfP=-*xB&gJ3smKPP8rHC^?a3*Sw`#iHx3?eL~JC_&Qr^xKkeQiG{m z65PL+UB2%c7&4({!p`knbPS*)jA-Nk7m_fV}0qE zS@o0!QU||$xjX4@@5B2OsuKjYscENQU$KTKGj>3QBDDi*)qwQ$^ft*t2Y`}2$3jAA zXb5u>I_ZqaRAa(u$TG~3&VKT;&-Nnu<&!hXAlN`%b@C0p0H&7tV6V- zr(>e|Jp&$ZhA$0#))W5sSKZ)NlEUDu;6^Nl$A#wP5a}g{zGq)L7wig$ddn1X8VehW zrY-dM_oui2%CoBIn0`5mdI7(01=fGdo&8(0Y#5{YxNxbpF%{;*FpKys;)c>(i(XlC zc$L}Fc-5EVIqArXB=SH1Zb zA}ngTa3~mwlo^V$tybmFo?ck%x-fqlP!2#K!;zW7ruuqX9A1QlOU@uN1bX&t8R*wu zKL!|bttuknkx!CE)hp=u_EVZ8?uu7l9AgjBJZx%eC@irCw;UL30f0cri5+sW1NSP# zb2nUcQQ;6#3-3mXlsHa8qF`l58Q4$YAq?LJzunRGqbEzhxZScE^hvKjao*I^BVp zW0t_sMR>xX0*^!zp%yqKf*;1l)?!y@*$f4OA4R-G0KXJPCNzE`f@&lKh`UKq=BhUh zQ_Wf*d%N@OI%APz18Q*?pqznU)2OXLRJ+!1gVMkrfr22^1q1d@*G-h-^7LI0!}NtT z?zo)D_iho@vAScF#f$*~8T=SwsQH!DcCdb$tZfR*G(8_4x<6$IZ z3OFWN)!SmxMF=X%7JgQT&k{zM?_f9UiF})wI^5*rqi+>+uHVKK1Y)e_U*7a!xa|N( zATE;sYZ&SasJH-*!s%cYe#xb9ygg^JP zmQRwzxC&!$wLJ5inoIGZnm0 z96+o9JNz5Szf2RC%5a7So!9*Dk}63wT%voQ3zCw%wHUXTHt&*(mDznN@-sTN5P!`b}#97Qe6zHq35 z|NFOZ#Q!*2N=7CK6VWzFj|TiPg3Zi&otYQ1vEi9L$r7DOUJ!^^1afqcGmL=39QO}HcfLuuiCE8t^!AMU+meu|dRuq5$o131K76&aC>LevR{J>#vL%X{UML4(E?Yu{N8XdZ&ryCI=&QV<IB}*e>oE^7^D>qbgfunzRCsqr}O_Q(vGN+M! zYVVa05F*`L;qM=B!eb6&Qe0>m)zn{OaJs3%cq}8*y7}SKbI0!|28P^x=6LCNH5!VY ziOJaO|3}q(!1esE|Nr(#A!S7Wv~75H8xi{*8z@c-8jZ_v6~(p{=$w!BS^4>kj+ka7Zp`@d2#yW$qnGHBIv_* zK>Uu5Ts^;cRtqksj!ml@4#zY1`CoC`mRE1jSU-}<7%(QSIot9~hMg`xfWUyIl**4< zz8_!^U%zGOt9>YZk-#UO>H3^?I$bTcO)Tvg5D);mD{brWq$k;jN2|zcU7L)43n8!9 z{Q91ltG7*I-{^neT>;Ty;`yaBsY!r~VkNT@j;u1a&u1E2tfxJGR*e2n@qO42-9P8P z+eptBiHV8kiPU7n+8mYh>s>$*Y3oE+r&&Q_XaUHUTozZE?M{} zl@N&;FlS9OOnaQ*;y^t|hJamQw}@a@`s&cS2D$0FBackzdGknIoIdHC=FkXVx!dbDTLs@op{qiArjwy3T zPN%zTFyM|lo)98du&}TQ4k4I#MS?)*{+B0_NgE*;3E8S%)lHR@e!n}*BWmfULNWFa zVBk zJoZLn*jzr;n%Y09ZNgOK1!a4?I_xs6c)v0e5734ol%YM*5+C}KrcH|_YI}aCVNeJQ z4X6TTdKV03dX^sLM;f*YC%!ri;HhoO`1#|9K$C07kon>RbKH9T+mE+x%+ z+p5&2L=CGO-?e3EgKd?2kG%gy^8C*$y6Y&MJjkLfJ*CS`R9kVeBR|O~x;7wrjgofr z!k%&SwW@2t%P#gm{Z^aRs8G=2tuBIb5aDBPDd+ak|HJfe%2+UezC%{jaG>+kX|9M{ zF0B0)7xzUmP&e$MWydAj8k#-6s2yHkbAG44zv_E>qMdza>bfhYh7rA(-*}2e!2SEn zmtKrJa-@hV20i&^)L9=teX9IC<}DY;qo&$A+~PJO@Ip$%O&KjZnY1(L`&zwmojP6W zj5Zr?JOBP|*SDVQ*RK!nJG&G8;Vmxh7ecrLEC=O$Q2L)sbi#3|FK-+2Cf#)9ISiuWpvGi@gyQK`pJ{^?9P8TIg??C^22e43@L z1XkZ>B8(6p?N&Y9(8og=J$P;yRZt z3kqAYy}nsivT$*vOHl|8FWTt6YT=l1<9u-o$VuM0bEl~BsMGg5cHK{;qJK~Z$flKR z@^Dx>I^GZ2=YK6V(^Ar?omFhUoPgGC`HogeE0ijGNC%g-WHNUkieX2WSu zm-z@w|>7OES$|C7DcArTKly)GX$bTCy}il{%GTRcARu##-Qsx6KeB7 z@ul>qq*MVqzs~=q6u-P|g)t$YzVGO?v$}{CUNMde=n-2P6CDlB7q@)d<;$0?s@nbO zw(FiN&yvvFh=vR9`;M!-seyq3V!LPr$qRE1H^pt|&?*%>lw)bF%UzZ%!J6w{U$5^a z*d)t0c>aEziW}5_()<%UZSU?lcrbXj_iwwbDYQvafw9+H*|2~)TZu!U zuQDY_3pi@|31YL!x(Xq#Z<0E=!W+phntX~p^~U7nEfj17MunrqQvjhxg7Q2F$MH9^ zyLbAnTRqfkK|g&nQI{$J@3(RL?43&e2mNq^r!SK>oU1}iy=IT?!@D;poKRROw(#aD1(`hkegaTB>^UuoZM<;6JCe7{)40v-x-TG$xmf$<=O9Yky4IUY` zmFt4NJCCDO1u9;%b}cKXI&jBmZhWnZFX1#oxf!>7ERE{Q8k--SVhBPtF&*Ezvk_0_ z47kMvFmu~1F(20@*X5$zqYELiwrhyd^$bN1v| zQ`j0VdD=2AE>1u==393Zec@AP5F;BF?=yDS<-VBK{uIiFATPE4@QkGC=bID95&Ru3 zTsaHZHH1!^(trN^`5H`E#%pO5wlnS|r~#0zV7hYtkpIkqF5HwkA>I5@>B&pY+&UQB zwXM>QPU+FJ=a>&ob`%Jc?mb#0qa3T;x$4|fmi zxi_WaEXt`u@7iCTg^FZsft=N1Q^@Z;v?JxOZ#9bud9)K;Q=!xPvVtLEnZ0$9Vu$X} z#*FOKSZhYR1OEPQ{{K8tQ4X~QfjhQbS&UGJE4jd&ic~I5Po`RFrW-8y=k$UWvYLgq z{C0%fbMNZ%x@0u$nu38?qZx z*ijU35f8(-OUV7eS40yiq7~_#!Gj&a2~VCsKY2|J)?F=CRkf-;@X@=8Tz{o!ifz2j zrVjZs1Pt0BRJ&DKTkY=Mi{SaiaP`m6(!MlZl2TEg59e5jk#zN-H(E&Pzz=RP61URj z;~1vSA%I1WAYCt&+b9Nca)%Z}9|`W=Yvv`}MPcpBGhe=Bz@-qBMfzSGfb~%9Z0k74 zX1U`(L=CCSeH}SofKoqyT~m)tRoSXZ&wTg z-4M^J>lqXWt*CzOe$0Sp`z(9JhG0FB`v`)SMleL{^T-)Qn!TZEUY7V-zK_G40R9;ra zkBS`Xt+HreKyF=s%c?&Iob+ic*}X(Z`0*MS@C(dS%KED2V36F=45{hkZMs=M{895` zWkmXURJp{N zH>FE0WcCJ}pxI0tNI9$PGJnz=%KU03al{{6Sw;7I#rsKyi*g!BG|j!gGmP_5gL(@9 z<H1*!0B(YepUB})ZG=7q*E z=%ujwJo`Cly?YN}jO+$Q`$W==fR^EV9Re|3X6 zj0tB#jIs4#n1)}@tt%t<0^qOx_AcK2A+P2T-LSOOQS;C37Z`{Vbqo4M)atSJO6Hx0 zdicxAKIAkok1aoCcK;g@|JDNQU+n5T`$XkR*s+eXi1Sa)kA^|sFd3ZVT}8jy2QCs- z%kqn3!gV%sM4Aq*qMiBC>7bj%qtcDZUTx?7+v6S2tLNxhV3z)HO6}*xKT|3{5vlsS zZt1VtRid|OyJg-I*i>ukD_OIiSVS#Th{PM+W~S-AyLZzB<}6BA5{`viW@c&WU)XfQ zrTGBJL>$sQ3%e%}tvc`fefYY1KDcG$+Qe>}nm@(TpCQD<%eUxxwn1paCw;}0jLsc1 ze!K-p&T_C*1lg0%?3EY+vhmpX51o&QUg1`bFW>Z%vQ-XKY$Y9Tzm@OCo;~G}==?YV zaZ2mhow}TmEtKNoUCeUCn1`8>VWi%=(ivZ=s(Aske!+Gf6W8|BE}G+Lgn}7D>=7_m`BIUl@>>^*Zw@xF_rou0mQMStuS^*7^ZSo| zmisOsIO!z5Y+r;>TrMNtb1P>RQlnA z?%hE*-+cV2Xkak+_YEoW%jPdwa^dR+Lm4fzc~v>8YG8ZxXia2Do-$7H+@4RIbn(_k z99(OJR808w@M&PM&I3;W=59=9`Ytg|kD#xTTjwzvJ^>+OOn!DTFi?^3ismray1eS8 zcMOGUbpO+fKA?GVHah*g=itE|z^PiLTzEoG5hZT+-e-R*myJAPeEEf^OWNk9Vitp~ z0ft|5EGrM3Pkxlq)SW~>3f{KFeV%@MNfRJR&4$irL`&eJ99PHmZPz(r4f8%BexViX zy6__sH_>}tKO2x6``*2ptgG2guUGodlz@^k>Fav;dr~~k>EpJ_vMbt|)3w7~4;r>_ z{W;4byfJ;dk_3H9k;B1XeVM{ZqsoEHkIq}==5~|m9$}Sbyi&A20AyySA;z{pJn1`q z+l0MQ+V9j?segD&<@PN+_lFNNGy5sbGI-s={ES+v=936N^+K!PtQbVN(6~@HgPw~H z@8rpo*EsYoJ9iENgIYLf-shoSHGik~Foi1DBME~rsF?g=Ul5?DQekoNk@05Py^$61 zEI0Mq)go!cYO6`}GJ4OXSM-6m;Eo*mnhfAA7TXIizmG3M{wZ3A!t}l)(VA}Lc0_ai z6f&R>3u}L0t!;d9#=+8dDk>__g@=G?5czEct~_Y!E%KSyyZbJa7*jGtd#-2x`Sc3_{LJ!OF;p>2R{QKFk_mN4OP0;hYCTT1fy zC0-RJp}ETNTJ4IA$_e^^308Qndg*H()HhsTKuwVMtL#z0O_<|o6Ep3wxc~#gNatFw zP$iO!5dfv}#qs@e!sFw6c-4FvWyp94wXYe}qy57eg`PcoUUz)FC(4PNQb#$|HS|uM zhTjH^ZuZm!i`hW7e<%|bZGTmBQ%Zj;RnwWj;$A3ZsP>xDQaq;FS-pWS!TZbZ8GSPN zC^*9UOHB(9W$aQil_fKDlre7mFJB(~_b#tre*@hf$Fw%4qNQwE9 zCA|vg>Q!j?8w#TO^!xl0}XaR+WjbGxIVCVc5G=_ALpztt&8`AQk0BmG>k7n%ZTA}C} z$kt+a!qJo0SMd_C9`8H{@oc_|7^CaY;n0Y?=a~*|0&d_RNyV|t*dmyP1pOEvk%`Tc z$H&^zxhjH%U0>Ns4kX3KYw|{&ItZe~sGo9pQ*cONU>euc_HIWSTdX)7$ef!(elS!d z(-pColf@AR5v+*vq!OS?IE3`5pMgq$^N%!Qe?!Uou7}%m2jaUfJPbG0Z@_@b>j$i` zTh-%!Pbl|Ia5R7U)qd%V3n@5Sdle5eTf0kd*0)(#y@H_1;ypcOpf0D=&%Lrydne7|bw{P&aj|_) zQYKv(jLE3$tE*>$3O;WBXnO`Q3!R}+MuwAq`vm*MKyr%%r1YAvZ<>mCim0VZFE@s( zba<8KYn5@9X=5sk>gbDExw)#4Z(wj+*zCgRKXFpIjASOT0F}iN*Uyq4AzE1dWxhv$ zUKa`;%!UXP4*bKLw14qZyXEy=LpvAIX_ zKHG3-j**^&b4CpCp;7OdQdU7JDZS>T)trjR+QG1=i$?C-vZXQKA|}n?*MimcG=erG zkGRG^`Re`q9}gT03JM5YT>wzTG5$5PEGjNlH%Q1)K(9h2LU@T+o)!)|084DJV`^Y~ zjv7UjeRVaXIdKG%?NwD90OBMTzc|0F zGd0SQvp-3FCFK#GD;l;mvbB z92}+0(oM|SV^;^NB2 z%yhIiDh1B)rD2_m2js$k&LuwCdjZ63iztKnooVrX!*ws8a2=)k)+$^_LttejIy-g* zG>y8UG_J|SfrkL-Znr}MX8XRKo!Yf~;XQl^F%AqJ3i2oF&f&(lkT6D_`EielNwS~) zg-Nr-zxZdxqT|!gkDr@J09>4#We@a-EmL~{+uL7~(auYh1HZGqwC-4RoRNl-D=#JY z$B2>Wd?){UO1mxg_D?UbPD*p-;a}{3chOcNFrhdiSR}m!mdm9=k_d$I`A zmGPXQuayiUP~~k-P5s^0lmc%AjARycHknL=5_zuryr3Nv2a~I-+2PuyB(;j7Mi0>> zhp*W99q3N9(HRz#cr1$`%pcAeF_FH)ilM3B-xH}N+W@$7? z)rgbYOEWi}-F(PablepWWLNB6urTs!fZ?sg4gO#K`Aba?x_x7wt)euqgW6*LP4xVER<=e--L9~g8<3JGua`g=3-n+j7 zfza%h?M+&gu}sn6gtU8PR1S7*)wV^~=#&S$PpqYZ6@v;3BjUKWjhg0-Ycx_{>wqJP zSq1e`Z0sQlzBLp(J0~>-@83uV-~fsM5vfYU-wZO40X>R9wN2Vndf3p6*dDW2;xj%O zc%`Bzgsf7vIlY+oOV>YL2S*;!78ts4Jj>z;SBsrp;kq>sP7 z)Sj;w+Q6DVpUKx~y4IspPir$_FbDq0Cwvf7i?+sb#D-1Ij9W%STwGx1@(D+Tzpf0d zQF~h~w{RCMQTXkF7S>&(x3AZ~Xrw*byw9#8<}B7!mupDQ#8#A`_L_?>u8@pe&PrKx z=uB{*HYa{glQ6CaU}V3z^(vTF(3mknwh8f*lMd$Sp&G5O{?Zk&uJ(cB2>X}!clC{o zN;fTxRBzTAOrfQ1_W`o+f}JrruFQz49gsavmSb_3+&EVI(dDYIYa=i)q<;LmjhZb6 za;lN{Zy5gU5`&+V4rp{0$tlW{-X9^ZJD(}pPKhYQeg38bv%?n|n+y7U2gP`OS(sY! ztHI1?L>r55HFVH_S$y!i1X_dTRi9^`4XLe4-40#qA73$P&YY;Iva%J9jI=HsJ|;S4 zGdh#C8z=y&j~fQPK&Me)c-ph~f7mFhoZ`sVeP*zwdc#G?_G3u5xiUd-^V-PrJj*6z8V&uOrsn9! zyd4-V6~ph+P$Kc89%)(_*(gGEIUppWCzNq?w7)Ltzk4B;$&56eJTP5!Dl--7#$!)n z+zQQqrXb7ylG~!7;LP|}C5t(6Y6nItf{&z8aEdYDbJo@%g|ZoCv-OmzL7j36vSX=h z!utq6$-ALvw;R~!{GBZ$X88bjCq(q_9y!!=1m0mJNV8)iSvXu4sM6aXle;O>DERHlVx@ll7dEnHq)vWqF=UI&1b1R$)r zDJ>QnmV_7kYkpQP7TIoT)sxDNae3D$Q5+V$(hKb0j)n3W9~MuuIAMasPdL6z7G>r06V%9namFPe7glvg~h_(M?OM5M_cG2+=du1pvqH zUp8%Qs?F{L1C*BG>vi(X`SWcJ`*a6<0K0@Drd#Posd2OK5jV?_;$6z2%7whR%~xh4 zBrmF57Pj-!Ul~S>I{Fgv7x^Od1R>*CP0mm|+^=cvaT0}CjiBr}n}4mVQ^W$;kcd@N zbSIzKuT%Uu{M3v1aO+jen$(e%UrbUN7`!OZ&qOdyt0hY2=U2UFA1Qt!nIP8D#KgpH zyO{y>`^5aLo%^`1p^tQ`NFy@TsvMgArLywvYCmI&z@8ET9VzRM+9&Qs<2#o(f(%3x zvF0Z=X{ezHLM{uVHA-4L4({309+k=Q z?Dc!0_2*zzvo7`Np*V`@D^Ih`)l=Su>d6F7rzood=i*d0LoNk&z92zdVGrzlS7uqR&e zSR3>(R&-;W6G)>D#)FUaX>u`-!=tCJmAx%7u{T*d*Vw+Tci$^*z|YJHHW+^Ej?yE@*mQcE94s!IMz39f6n6pf=E0% z+UR9fsNQQ;_h^%05lvfBm8qJXqu9`ydg`!o=!(b5j$$|>i`ob$!TI8w*)YWJ+@(t4 zsvnCIg0Z~@kyTfvR?bmMpxbxgDbjakx^Ih#=_<4XEogkio$X(I5teIak_KDONwcX>SN zN9%ssn! za?s*8Zx-hR76B;yc+V$h}b7zk!KT$Y)Y znbSamcz9!mFq?Jy2TOL57rfeh7-&gQQ0cFU?Kl0a8`3gxdPMjs^ddi{HDkQjm#yM& zZmCfwIa|$5w0J&bQXJBw+ zN4GK3Cm1b$BKv5?nx7CY{+x1jxY1%KXf!MKc}wOu+uyV*SUbGa<;d}7jRNyDU+^xA zo3rxja31w=)ajK|r%aL2IPrdDcc!lDs~@@8P&W@>%I?ugQt4$%=&i1b!;@xI937Y&MEM|6w`&NlCQIL`2g zgMEi8TN#%tnGXLQJyt93IlxnJ*|Gq`Lf#LMj004qZeK&ECMe}M5PcWg7QNnnucpC& zwE(xaA_+NS8BlSLIh?~^fku`v2mb+z^t+7!)hIemrcwYT4C zVZ@w9SE{)jTO>Pdc8Rh6TTe2&*s4dU7tM8G)U8pi8A;fcKF-I?&obp%J2S04;9%lH zVjiffZ0N;Q2#USNvMGT+R|DNL+1!Vc8k|6eGJ1FaV=A&;N>JIRo8t9b4NZlladgq4 zP?~_-vNZtz7q(Rhw{5knPwxwVXeq)^6naslDRwfMGI0o4hjc+u*dmH9xZ=508h43p4(d`!$#!s1jDAskX5tj*T3*f`Uvf?>u}%P;a$d72Q#!=Y34LeY4hH1%a$#} zKZdhzFn!D}C6{Za4+<#4hNH&mQ( zQJvW;ltzK^en>PC(fMuFyT!oKX*~7t;KeU48DTaTM6vnyLR2=i^jYPy4hbiAE?TxM z$qk}l+a*^;(dp;tasP(}wCd)zU4*nc%Sz!d(Ma`V3}kDNs#!wv$&>QOg+)a^$S0za z@2p)q`CL*ZQgcv)Cr8tXNy7Y_JRY{52gM;98KZ`nhBBaS4`tHRaT;4BUZ+6!9G+jW7P(u!I<(bIv$Hcr<;6`H zPakP!rVxw%30k{2Y=hhaP;C`S*Ql7qFNhyfggt%wj8EK)oUK*rq6E}+h&C1Wg^-XD zwdkywe3qOQ>!>)laV3*7hqxLS+}`g1ohJ%X!+^KC_ban2J<5CTCmK1%YJX+CAeo<(i*I<>t&yf;NEiJxVO8joj&{4$XUEN<Bz(FGCEQxYGL+{B?@_()We9kC&?_P(;IGs>d-#B?g@1ZW@|kv;EL1Sxf^Ay@TF(f zi{i*s;)Q5vXuPCh6Du4P!?x2(=>?*TDQQOnQbl~OY0U%_7+SN0wSbPXjxvvPbZ0Kx zvlGuy{G+(g!x)R;KPvGX$xCD}Adrs1mlftOz_noJH`FXsPtT8c_i{nWYO^SXR)^)& z@yqY?eZ*B{_nx;8l5~LZ;@x1F);Fk~`B%JsOXxXt3uDx~X0|t3bJA*STC4Udz-QN+ zHG1ogYaP0`4~Sy9 zd}~vFK=F$JipjmuCTXMD&kV#lq}QA^%75o%DWsU+Q0cyVvxFIf+u|8p@=WyS2QCO` z8G0RUYj5WL;{D-7?sD>CS5H3WTN zVc{HW=k|L={6(c^lX3eIF|3<=1I_46A-?&2`JB{hZ$Gy*Gt;^VxT&MQ60eBF;@$n` z)&mvlvG6#>5Z#!#CcHvRjNE+?|2MqTuAMvM7Txdo}@?&&B=YmI8PKZP``fo0f=lV2dFT4A#2VBfrxdh z?GBiXS_oV3M@Jv>V`KvZS`St2kU&FdsjiWkF#GBir^}sMD!)ApdNBK=--y>A=pLjm z@Sk{c&hS`A?#+>M18@2=?XkO~tb>UjfKc_tY8jBv*7ykxX4Ym?7o#2L!Asun=E?SO zu?+*RnUz7^t9IY{H@u(ji;;WK>5GZ7FyfBnnE>=jQAAqt`b)1LKTkR>nSg^V$Xl)_O_YKLyEGbm6oP#OUt6T4=R;e4JJ^Ak$;=SyOj|O+UYrL z);W9AnZNew7~*53hDIk;+P2+-JgJMGp7pnht%Mzt?Wb0~9S^n`eYlKPD2r*Npi+nn zn)5cumfC1Ue8w-;kyvU?)l+{wQ>ev3{BQ+&hdq(!^-xBJcD5?LP_(U5|7 zRPGEeq?qMMB<9F&oi>>5(lnYIm|UV2I+D|1UoGsbG99tlB4{8`&fPY<87sS}ro;s^ zB7~l!x7UxiZ5cgk4TP05!uSU0%bmEMz2CpQdR1QX6_(Ef)}1+wo-^9x4l8sH_kf^^ zbre{O)9Ap4Infg{e;PpHPy~4)kQ$BdM&SFP(>!s}n8_T!%p${*Hy!$&pUx^nSfa2s z7k|=v6n_4EV)=d4fmv5P*W>s1wt5Y++VqglA!x>RhP>nwMA?szSL{5rBel7z35?e_ z5wU^J{B>pn-(l?WCZanLPTptUL^K~nM_KkXyebS?Y()RMSD-M^GTpCqC0KC^#JXft z^)7Eem@Ek11J4G>Fz?jAt9j#9*vIyQrb-*vV~AHYI(ZT&-D@>eT1%NP6SkapiMD9 z9)N*Q{17uAoF5)KukE}T*)#$| zKH=L$vqtyFeyC%X?u56)ChcL1er_Z))uX}P%y%$nUGB6!6M=PMZR5JyhY$Sh&Alb?taS#)9QCAS`ku>KAn~-_5`iDu^{*}aV*y<9@x1lZzQhbNAH=JRK;{1B;gfaX*^j0 zE|IovIdDK7V(sUxYUU^8u^=6`o>Ik6x*NZHJl}$lv9q9BiECy7{0Ko+YpSI>1T#%8^OJa^$u0J*&bEF9>UvlY-LTQ zY_EI^w`-zG%C)<|kJs6iJpRF{BGx z?7L-43<_!de+MFTG-`2V9&i!$!`X#f`%2Bzw|KtF`&OnuTE5RBg1N7|A`>lS0zl}E zq<4yRqzJNILC8|>(+yw;)&4%cu`;QcvEpLQ%5Qnz5eOccRn?goRfiD@q?rvV_eck< z4{j(5J$e%O=&tMHlJJ<4Wy8GsjN(YLpPVi-*$5sn@RDimRc&&hAFRo6+Egf8!TTUA z1(1CDbc)Q%p*G%;loFk^tPKLmAPQ7Z1J}UZ0qp#qGHsgFDow7L4o0kB9LZ^^Unlp( zOgRU;2HF+Ob#ZZ7KKNKmW$S{+*a9(MRhQdMCz4IEagr_<-Q<)QW3e_y6Jr^FMn-|mVOvVFZAHSKBN5r@pakPhX#+cDJuHJ z;zbZkQGTh6`4xg#zN|4hGP*SpSi}r+7fZ&ZLN#_A>d}t$-lk1{9i*;Ii8Y1ddf7{J zXBfSrc-BE;Hhxb|_x~Cv1`OV{zX#xh(8XQV=Plz_lwYIec;&*CB2{f_tzBIJ8w_i5 z9ok7?2j!ea8IjQeG7c|@J@8*`8?%oug8Qh|QvFcK1- ztEum_x2($2Y+YY!L{h12y^)Ta8G1QK!D znR=z<>U|Y`!?$tz%I||X*7VwKpd=%`?V0vsWzU%1y35+@ImiV1Vc90xppFiS>W}I7 z&nCU@uJ~QQdbQu&&a2Gkt!%8SGWn)w*U4ia_1bl2l6|L@nRizjN9>)qM*n%v-Nc*~ zLzZ6jYu4w&p?bO(3a``+{HR#k>#AwZ)IFPemEAWm@G#B0<=(Aqx#?^r*LMesy*~41 zwgw-X%&34EV-@86Ks+PuacZuY$=lUIENf3KqrStObC^g?V3@MilVHfowf_b#zfr+yc$CT5@udLcX(Vf z3K(;cie!Su9cL@^nCv->tO#c0^k50Q-DGh_cNOJ5I;N(kAWY_`)&XsqOQA+a;0wgs zk!zF=@YU@f(?tswY*}{YhsBO*W{16nk2YGo)2WhbRW{(#Y0f&e*CzYD+ms8-HH7n? zYOoI+odBt^>vY5xfA3~&hMY0W>&LuoWh*13+11t6{=swG{HaXx{xVKiQ(u8i9;Ux5 zR2W4o&pitX*v!(&ix*ao3Z8|GY=hn`vkLKRUm((*?O7SIIpDCg`(kF=MJ1VGNpV}l zlh{7z5@`j;&#QcXGS-fO2X~md!vRANAO|+p%AKpwds<`N-SA6l`;D6msJ?kk96TLRc7F zkZ?l9R3CG>ItRvgpZbCvHM#WHZL_$A#P%Im+U~*Zk(SN@X{Lo8?{LnJ(Xi=ov~RzZ_O=p2N;pKoNIgkD9aAP8xZ&(_#a1r5aoQI`~9|3S5a z$52RZwei4#nZBhhaK9!GGhQ;zF6PfGEMKq7Q9M>P*(LuBidGX^mU_%Sj&|Ogf87EB z|23jwztx{--0^PC5Rp|uij=cXA7l&zyk=|gT$>p)X9}m5`nWj$o7v+nESlhHce~uU zj>)<*YP^e5opS=Z9h+{=J`JQNW=J?>>BOQ>w15sY?mM1nY2Kj27V8HPUX7{af-HP8 zhyGHO0X|SL`BLU@c9ZGpI{e7ImeVd%#Djfx)w}`u&y=QlY(c5+J)~33xOp{L6o#Ey zYR*bHW*Jl|#dmtx%omyrlxy_jR(tein{ssVPrPu0yqxne`rq`|i+yg6m z(FmC(1mTu&2VoNqtS%5BOI*}2RH7U>m*$T4@Js&Uq`9P2%!99UDXi(^^sl}XtQ z5CUTUX(x>M_Q~G0UU#nK)!xOvNN`V3aNw<(hsxHsZM&;@Xgc~_0-p%3A(2FeXq_kM4TWPI+%%^GR&SPQ%2Y)>;zem9U%1e8Wb^*17QOhdcY%AJc~IX zYg=60k6KR1VVvVr6J<;*^|7{Q(IcQ<5y*eNM6#0Eu`j#}zT^0>KV!3<&x+eKgCa5p z&q@ueX)lpaLD%f^Fc_#DAYPpwEc4irHw5DOFKIH{TOFlpc#7AeTxTOMY*|^==s=&P z)2mnbnJaJ#1cFKL*Tn0hy;^GtAY~4iiy_P%NoW72a!kyl_b1VK$odl0*27n= zI(N8|Iql@7FN_(vwwku0Us-M#kD;kEFt`AG#Yq9*5McG+N>#&m{wD^!D|>${*wC-}jpyRddA9 zZHG^uYzciSe(}O+P;_A^Qy=dC)-Mo?u({+!hp%!F!Sk2@^elivG}u z2$ssOTIClOrb0laz^ZSE#wsVQ?~bsr+Uk{8kRjvrCqHyejRyt^tf2l9^3t0&l85{K z`ChgPWTy7DEih&Fw&Oy>z51PtK7O1LTU$mv8o2&@FCni8XENdf9BAb-b+(k(w>WX0F}QM=g2`;9-+^V``ed}Q$!pS(yZoBlkQR_a6Sxv|>P#z<0^L4Rw(6d_W^!shFl}CABd5IhE%$UzV)(<;$1L<~4EJSyoBe89QyxX=kCb z@@9JnftZY$Rhvv-Qj}U%@MyZH<|GlR>tl&McH__v`EYo%9@XqyGEreT4ln^Q3~* zRxLSp$OXcVfs0wO6Fl3R zAA1~){9W7IF>fNTQ;1^9VA)*#_&Le@BtX1E6+-BLQi$N}7vqsz*_xr%fr3u1Cm6R-8vt~^@r&Le|$GhP@8cp45 z`SNL&y%z}3Fd+eU>~Pp#DKr%kj1G)WyECwIIeR=$pZ=!eT_ACVK9AG7957Qhq{-N3 zK!D!UU%^tUlM`u%=WVa_zg><9I*aUzI7!WE2r^@FoRyhgN-NYpuin0GNY3isY4VgQ z{yb}5VMA$UIA^Za<#W+p50A-ir&S7l@P*U=VP2ZHa)79#1ulRn)4#orNtBZ37r%AR z{3ZWhY3Z-rZ&zNwc5s?E@0CnZGW{W(AfH(hJjGw5JNZ%)(<0!!(`ahQ?QqWUQAOUy zq%Q6wr!!+I8w8m99Ur)61kvXZ^Kt7;U!`~SK-Vv3`;@ZL&Yyt_s%cdYJ$CL{St&zS z+&8W9^Sqp%tSaA~Twi1O#wpnk)R*>8(Hl#J@$av9J&b1i3}}8>LocR5{>LqrzgD!& ztzz5iJVTk8XL?gsb}V^7)xw-$-y=FhforNLWt5!xV*3CvNWRtXS%*YOZnGMb* zF{;hp8*t{79&BDQ7#Zc-;=fver|;h8@B)j_E}T4HtPyVZ?lZ>`8gEy@kpn|QFf9bP z5R>zoYlX0;__l>9rJBtzp;6?S8nB+h_Z==HzNp8EVtjkw`AunG`KY4FGi)51ETX3N zRM2rzno2ITMK^f&2dnr0YXeZ8)~v6rz9A1h88y~tuvkk7d%CpnIl%4Rt@xo*%* zZOw!PL|Tj|Mdv(N67q?ub(*pWortw7eiYYxdUk#LIukR7Z?qfY9JR4)t&$92{QK+M z3XRhZmVKNs(&|rSnM%T=rA4of?>cm6yvpwT14&lGIgwWMtMfbbUviBOW`4PQz}V11 zMrZ1Vd|!@9+w?Ufx9Ew!9E8#NWYaxnS_MdYWCPW!AgA;wFqVNH)%`jQW*Z$eC*udr zSrA|xxEvVt2F0B<-bX~VZEYaJ@3KoszhUb|3-3L6;EQnbpi27?`k_}ZR=;emenUr$ zUnwd|o}E6z0sz~fTNxRSXd~>FvbX~8bb(;Ibu-`O^e*F7Y{2r4HP=-lSDxGHXg+!M znZ!lCbz1*7A}WWT`To4ewas1M>r6CFZEf+puuaZYFZBT=`FlKK1 zp}#98d3?FmHtk7w-4G_p95JUGVIZtNL0)<9aA%r-8B$4x!N|&fOysZUhOm9qPxENitM_iKW8Al z<}8L-;c0um?ai{3Z_OniX6tPW29m^MGKAn=L}|;iMQfu7%H)WildW>_8Kv1qGeIr0 zRjcxTz|38TmUlF)jL%%qdXe6e*=pwsZnxbIwf&!8meYFndFOEd`j$+VE7tKHrGBAy zfl4pWeeDMBaXpIf6QlN;XZ9%7k?$=d(SQUaR<<0n6;myV-@Mv@;7wEnfx%ra(w=L8 zpd37SkR3u2@|dU%#{%(jE@=Q8_s`CQ_dw(*^1znN@0y^^00IkS4>|uz!Nat_91$k} z?ruaDYoIPzE_7(fxNn`7uXXI-C2YvIb;%Z6nvTl(^65uM+p7zj{x_VMIW^BU zE({CXv)fCz+1}#kC1;j@-|*||fNOV6pZ0sT?WXv!6Zcs2Z0V*7wk9REKW1-~WM&x} zKS#>jL%~HP%g|BlNGRo6Y{8TlaPDq z)i>V99-Q%i@ziOXe&CsQ$pe&3XFlv%m3RA7$5(%{)pTZiObWTPEra!SuEf-seg3)h z7o?K8#3zWp2BJhB743W*$D^bxKXs+8hwR}@2Lv}L%pQIgWI|bekcGyAdq86XJ_UqpEJ+1dbGb zCn-1i6EuMgs?uR8b>P`?ecFi+*|bFavc>VXy2Ni4%K3G(@3nhI*nC*#QUBRWCRI-) zZ|!F9wsK3-DTmqrw@l4xVOxVS{fs|oEG@SCQ|D}&!PTxSZ#B+JIMr?J{EpPSUt~`U z<^|C);nQL8D6lE;Yi|~>rXZo2Kc1WP&sA?zCI%-SI!lxGnW^&+?u=SU4R};LKw4l5 ztB7^K0q8SGJCW724l82Jr&iaJjuO7h2+0ubQRI?UBIxkP6N zJFEZBG9Q`HnLi!d{J*h!z~hUDJ|)AX&Sj6@{Iy-94CzX-^0*OBMr3vWjvt zvppbC0A0C{#%wS4K+Wa(qX0L0x;K5;kJtN_tLdq4w_Dof{MNG_#~5EeG|8nyK-m8x zw(@AlUk^OS4%xZU!~JpDpktk%k9b$5V(rm;{3fS#4NOXa7=|NdGaD5uOO>!8J!+HG zT}z8~xLF(Uwqt;@E&EerXVF|-L-3<(JIQI}bS7YuSunwBFAgKzt_)1o+7B z8DA6djv9~UsAd+QspvDfhQ5I9+16U$J{(JWEfEKRMlQ0G^kT|gEd*1Gj|QR}6cNz8(7i*$ip@_^aPE& zrTx*&!s1kXkiV7+@>80Qb}4&4>G_V>NP*>6R`BA@vOha-rM+nOx%09Oo+YMQ@kg@j zo_0K~_jX~UK*Nq7#{Dn4zn<52vEB-AHO+_bhJC%fQ@{O3V?~PxgBl<5`0_wkT}|5x z+O^ND6n}=DY%kAmr#WdJY!M}|BM>Nyf^R{zPkdEX^}v@~hr8BBVw@B`Zu=D`>8MI9 z;)|!eGaIcLy?_VHaTzGhFBK3EZZ2?^JS3hb+dyZtuK4zCyx<)O2QnMLcj<0C-uKSi z6`~|#d_t^HAihg1&9om>e7?r%A05THVmU!e?eY6{j04{gP?YgiV0hQ|=baTGR>0Sd z6YYDt8fzx>0bHd&T4Jbl>d&7)Ygpw{_`x~Fs|jmuhpa96*WYOsG+WYdto->;px(Ph}uLwS{lp*91d zJ{^%3(8MUKZ@JwIe(du7Qt8$B6=6zg>xx=TI#6*C*yy!7JrU0HDWg=B5}O1Vc0G76 zC+BO3(gWsaHXzyLvpS^#Y02ajZ#jw+5M5-li-#>*2RN@TCMGHQ{pYDzzm>KUW0HP* ziapU6N(9Fljo8=_@kMs1|NVM@qtc{%{`dEBZ2+}5!0_N8!ut8qGLX)>=E|K|kq81J zb)!y?9-&>@E8BI44`7)=Pu^K~HUAO zmRLc0XcWoMrw%S%i&DsFG0URL89>Yi0-Pqr#0go5wF&E@1q>LR$PK!7ZvHTaD(C@+g0}# zhIowFjG&v-8uIDgm&h^f-f}VuxSi6vO&bRW0sh@NoOyVnj_lY?`JTU6d%aDfZ^Nv` zP0B0Pq8yh+1-uwKW!TjYgT7k1wOc(<{n2=*=qWLeN~itrewtm2tm@Xi^MhfBqJHgA z2r*vnWzg}gX7G(gHx;KmcG17jqxxI=IF}B@SPaQt0ekcKO1gU?0~5mj749z3WOlTx(L( za=;430TGENAD-Qen((LRz&1_yZ>{@zR^y7)()43}vd$=F{V|Ukt`fEOz_TV5f6jZg zHPQ!@O=o=GD)k`tkw}#4Yil~LAUEo_(A)OEbeU*>BPZunZmF;`l>EB3VTbA3T}!ew zgaaie+yEAak2Hd}9ht!9Ks?8ZN2f6Scdyjx18}wd7 z{mPplAIkRtZ>53+QmW5V(r&U%z&L-B(~79IP5M>4L@n)67OH+{UE{$yrbbh=NA(<} znSVL@d{W|dSQ`Y5yHZk2@Pt8O)|%~jXcgG)2*}x}&cJ`y#6lcmeP9lyF>Y0UU#W9K zdsG|sXc2kf?ugK+_$Bk+7yoH!Jh=DoqP#(SuK7+WOlx15Z{Fo?^NBO2{P?-kewa(= zC@;sT(LJL^kGb~j)18dXXDv5}&8*CylHu21w;6WP=l`>b2;;gD|9U`t&lqyxue&*@ zkyuoKp363MfZ(0|^pr#07z_JT-M$@qJ-&2U;)$A#Eg4mjEd)Z|Q~fir($p%}Chew~ zS;T7gU<;;)dO;Q|N;|JRq->IIgv^K_SQ1qqo11*=clK{EN8k;z6U@yvm=+pft)~6Y z*kR$W&-L)*@tT))H!%spEgPpKL{L)ErjB>&86S$$l;Q@c`XM793@(3;nN%2^)3oH4sTi5-~RYJ-v&j?g02jiGhrqP~A`kS{*^C>( zD;m(Og@%pp%y(>j=0gLwj}04~Yox2c+2HlVq0jsFnYVdgr^l;p=gb{7z;N|>P%3$? znKAaq#N!e&6lDmW6f?5^nFhHaXxuEepD_TlyF=7p5Jb0d;tGwpjt{Q+)VuL~YvQ)O z_M_LlEPn5kqZpr@XM5)O;K>UcDTB<-!vS1wsgQU!{>_IueMDy>+bzg8R&J`%#UH4e zLBMp4{ig1?<0SSJ0_F?Sk1~NtZymz?7O;J%1q;TU+lewjl$s#;GH{F9ZrAa1s{i5W zM0}Q%@-=Y#Xje0*j!8l0kcl*U&_lMmN<04Ug}9xoy2!2i z-IC^4CfwinojM(+UmO2p;n%%E zy$fG8T=TQuS&xnRLx+7bj#xJ5*X3cCEj!-c-(_5~f=%g$ZcPUb+&RtPSN+Arm!W-) z5LDkTH&j!SN<+GI9QE4ng{62+?2+>^w7Rf<3Y+b!3w>G?e)`nguZYdI z?=S40s%dO>u|a--%fi$47Y_N2Tz{zjf1j}b;k224W^bVjd{K%NhD{hMKGVQc>o$?h z7(EiL0x#J=cv5c~5Rm|K@$C{O@u;N)A=0$!_UJ0CC@bOy;9l2D(47no&HA?=JP;dP zDeL9A@rAU>elz+ct>}%D0Q)o%yfo-)QSa*Ed55MbrR1)!cggU#`KB`wMe|lRA0Mdd zbZl|6do62Ew)0$d*kN6Vwu+0LT0C^dJyLTd_4jpNi5}vE7>{uW_U+q>n@1s?=nYV2 zzX|L~^z!nHJpR|+-KiHC-Q~?4Jy!N_?CjY2-uK#$t*c(Sth8U)Xj$H_E@4xfJQyZ7 zUw&ytB$U0=O3-FmXd0_S*b@%t1k}`^j_53jS0@=inPO#B;JZ1Qoo%QI$1R;Som|A< z=A>z{R_gs!{_me(FKhSCFqlpSER#e!syUIxa~IV${O)yTW8wYBS9atjHEHj*a7lNs z=#y57H%4U`l;16Dv3$s=f7hVzxd=3qmYLaNS;`kb>;9*I+rfd#1*5`}>Vukq0$hHz ze;#|kmEu#+&MRM^8W#uNs`k$(A4P|6>n+liw9(H@8&MvP7qKoc*z6r#r0}TWcQa`Mtg9hwf<2 z{mWQ5#y$CPvSLJ^E5Vt=1DYD08q#KA=N@jJ%`WzK9^|X=V3^Z!TV1<7d8jazJ9nOa zXfF)3UBKUy!GFDlVu-d+02`)~)+#kmL>%CIj1}4E2?HaOspZ2iHEh zckkZey?ZRDk#+wwu;x#K3=E#M6wQt}eaXg0I$W`;chl0`v0?m?8&p*Sl!Ptbn*@YT z7u$lhRjDUbY^$<2pK~~?)M}}_`7yuC6_;jB{qRpGpCFSKzSCk)Dtg-;&7QkzN&i(Z z`&@he{Y3fdcV<)hxCv^B_FKiR1*Jx8Csik1ssY(JEDBEsuDXgPfw5Q^DTmIwX`++H zDRecuY~~hjmAo@P>)Q9-S&C;5%-)=qHfv!cO-FZxNBT*b2L`vL?lYRG_V;-rZt2_U z7Xl`y%~S~e(88jh8s!$QFc|5&?6QWw6W-MBuuc3)-U$`)jW>IrF_%(au)gelDhGD& zME`{rNzGmz7mi?cM*~=oWfdAvdIXirUv-^}}31u)Ht(OuQYqd4BmS^)7Cnn(JD1 zPy7#XVW&QOCjYa259HLcQ;X9a9b@|xB&!_QRRi4&i(nHDlf}By*rBuMPj|E51~jD! zrfd7{JJ^u-M~|W&EU7c`hP7XQpO9J8-nPE*+Id9>pTyt2>Yn>GSHqy-QE=F)G3Cy0>em!q z8~NNW-kdNF3LrsG<~d==+*DI>!#5(5+>I}mi{W%?>6@c^&faPTL#ta>*~ZoW+1@aB zVYEpXL!Zlz>z__}qPlmZ|3UiKp4l?wB)`UX0&U@K?zf4*$@TzASjJYq@7;`n4bCYH+%6{Lh7;7eB^pGzBF!&^?+g4@va9METyHWK|?|F`)Ti5m1 z8(|QhapiKul5@RVD=#>0?et-kA#XTzmVo)Y_3EP%Jz?KCmX)yz8X6iG)>S6_XBYU} zok>qLUN7mtZpnmO8`t|r7g^5VFu~;byk{c}sxLH`I6CAGVr%f1|A`~^nEds!*i(mc zSTv2S)KONCwV??RLEH>hcX4`j&8@5yWGNkECsg`9K#|48iU7O$cxO(}E_;4{l^w5= z>V4{JK1yd-y|c}g7p%-GI&b^&>hvyu4@P@s3Ty8_-y(qq;!1Qz%z$j9)gOUV=6`

t1phGyLYWj;i~zU)go7319P~DypPi`3ld`ZVfblZe??s&Fxo#^1Ar-&QFLk zGwn14V2qV*{L3FjKrZ-DaSBOEui1v@|0i<%k&$wO_yBAJ1=EpXSzycPtN|@*jSqeD zvLEt%Oxs_lF9mpNIrTdb)OEnh$7?t^DXlA8=B*sN%(x2&d-RxNj~(ddr(AN2=jZ>= zs%L~g$N&5DyY)$J=Tl{JZC~R~uMgh!h|`^Zdqd_w10BcvH?ui)uGxdvPI(Q2&v}h7 zXqg=;Lr;2+>HZ?Mm0wP6{=Et#p!fPQvPQEvh4{7h`9KxcbdluzgGbtadi&;$)CLCc zEgk_P2!u-2+;(qKl9EfEM|J3;1p{o_LJf|zf9k_kdr{1?`bw=_{ zT7Ub0Y`u3l*M0jxu1HBLl0wJ`N!gS%jF7TNB4i}l$`(Raq7)S(BV~__vWk*X$lgUN zWF$#R;rDo|>;8QI_`UzQj{822lK1QN9OrpH7KlLRXt$Hiw+4bDQ9d^mhUV(Zzc1kX zU3Um=Y9=pmU)*$c{xX2 z;Tmw74}>r3HsJm5_`+)K(Ljr^{I5Pqw}YOR)Ou_WwY0RgMIgVz@O?Fg9)72q*uA*q z!{DrpT68Tz#Z^*~1~UY>JCI;s{9k7ZFNN>W(Em2tbv9@Ax(6X||t?kkkh$g`%5YdJ{ ze&pm4UC7}WlCZ-h`-S@L^ZERoTS{IU zv^aeqI^tj`IPr1q>eq)hbd}!RQ=HUT^N6>9TQ3S%i?tA+{+_A_X%Pa?<3AS|{ym!u zFU`N;&O9Ck)D*WQ1oCJBqRH_A1q69BYG-{OdN45GM7u=%R(S^+m=O4k&9zlktj}C_ zR&a67Azm$LZoGo(!3vq|P46?FaEk{piiQ>5VT%jLO_e_#yViR5QZ~leqDq7v2#e5J zy#dIh3A+pQ4AOdGH4!RfGa2|~#MQWL`ipH`5^$-!RH!AE}k z`7*BX^@ogK$p<&?#J^>CtI_s6^H)t;zO0Eaiwa_K8KaV-y3;Ffu6Sopx&1>i`@e6@ zC8}LMgSTPvoL#DD7FnOpQ35;p`73dKkl1?SP~^Ogt3Vq(Cm;p5*jB;A+tSeC2Vccgothd zZIp2AQ4eqTumeO4hh~y%NI2Im7y#B;w5of9o=Z)jJ2G&__2vFPa0`zKB?akcG3FRg zl_Zyci@8A2deAA7U)*B6$Z|8ZP%eB_*)ypy%Jkd`x8oP9=n)R(C}qw{k^ghM_o>xu zjP5P36#Pf|-c^S!{P+>NA!ycs2#O$Lro#4>*nL8l4HJ4wqR5j#;;ZEpB}t?>xr7`+ z>$-W*zZ+;S*m4b4zMTsdNxP?A`4}1Ztx=g#PJBBh*VPqVCLr zZd-iHizj^=B^)to_~-)Tq-*H|L+bt2adk8#?RuOHdclv+|ScUexU94 z|Fo&!Lzl0ugUBU|e6#>LL&2c7yK`giAV5+Coasnko@EgtZ+|4rB6OxwvdJyBOf3VQ1HbY1~i zf-zqn;tu|RbatcjJ792R3J0N^p~DJ_E_C%ckdT%DtP--ZqXzqhgf=YA)gDUh`1Wf9 zd96peD>TCJv#cKPTQyL9mOHbq{GQGF42NL01?yzLl%bfeW8yvT*Dq98rYC&-+|SB% zp7sCd``!mc?mkuV^R&(Hi8db9qv$S4O-FE;ygUu8=$lDxpB=w_JW3#W*mz<^zaqCG z+D>8;a?O>GYI zXVJ~buOfp4p6nh_alqOj!LT~i2pLdrtjx=5L6H9@ooA8N% z;n$C;qG7-X2Hft80d%&2{>kVXa4?KjdD4l3${h1$Oh>QZXD5G9cY^_2-jeYOJ>v#> zXBAH8WJ+dbZ>3c}qeBrjdv3k)o_KrJj#^nqI;LS|!XEF-mj8nWTcyYqN23KaVoU;D zR~#Cw!gMjLpt@fC-lBwPWD$I2K*7;qpTISl9;}{9BEA6Z9~z-xp##<+;je;gM{&j zq%~r}jJPv{Po5Z9%ym6m3?x*u>k;JiYA{mEXJXAw&zq2A|B}(3{9&sOhl!y=p<7@; z$IKMHb>R`^rhfBFI;k^`6YFdO=g@i zsR7`Nnu5>-yT~MvfS!Y{g3jW7!JC8fIyzB3;sx*vGJ`K8Zx`4TvCTi+P=#;4{!!gp zmHi6sdQ;t>EO&bQ(+yMw%c3)D(0>-bjk)^3Y6 z8=R^h<$U>Mjhpdzk0yW1$I*Q!d&-Ue-{ZY*BPnlnREZk8zW%85I#FTFVo1M%1_o3S z0q99UQK|-q`}JK{Ws4?2u+v~$#JwE|fD&jZV8g+`B$H?@1Nk^LzT&V6`x?o*GnG-6 z5&8pdl*T{QN5s$K^0CQD6SjIo>D+z8-(jf0N$l~wZ9i^EQAsG9h$k*j(ZSynVrY{! zZx{p(efks#FiqwBr2xz^bRmv)ry{g+LKzvc#JFW&Xk;E|3H7NvNx5Ot@e zm$~@L6?X9lFDkCG&4=jP@A)CC(W@-hQX;(%vT5}9048N&tiVn3-zg| zaKl9-$v3b+6*dorp!R4WCby&NFXIe49xZFlDHKvD*7 zE~!#lG&7FxpX~=(lQP%X-rWo9d zYhUxWmV7t6drYY2c7BhQ$!nI1i?;M*c457)9X9@KXkj>wU~}JBtm{1_i~EAWqbk>x z@1hbhhle&M#J166xVFK1Tn&GX>-@~*i^W{&E3sC1xBKwN_?36dO)gtYUY^`pG$AOQ z8?Z~dqd2|2>)}YeqR+HZsJ8Ca$K$v@;xbUXE1|*t_sQ7KE~~DfLs$O$OBqS{7~N_? z1p{w#b37?2360%Qfz9_Wexrfz23P@?G(#j0&d*=~1;mXHM~Xz5r6l$kg2>+q z#`dt&X8d**)0rrGy7Gq1XZ!g!)>@Mcn_!>@No6NKNxI(&GYel2+RM-YpJ>alZ~f@g z-J3xrqMu1zGdwGWgHE{|AeDS(wt-j$rX85wA3{3l(I}O>q^C!pU==-1iVJd7QIQcB z&Xyf#qPJrhG5@9f+ImAR;SV}Y5vh-2vQ60bPfVI*f}mYlFqOo@!rGglx@Y7EZ-$J| z(m%xpGoKm>$Gt!}l|+ywP0dV?i{f~kBpob-UgGXJQXAR-p{5w*7T7**f;aYHwWR1# zBw0cdbo->5g%^fIn=reTDC6z3d}C!*=JAZV^^v#ci_gWE6t4BxcLya(n;e||#Zt^P zSE58G`0RSv*C)1&x-5urQ*o4;}dHeQ>F&7B&AjHYjVh+uqjcGdNR?EP=0tU%IgAUCY zz(j!1{3!Xj%!ei?!+}A4&VCBpga_*PB1p@LlyQgg>z`Zy>g$LGQ~HW=Pj4?5pbZEy zU>=i+E9i|2R2t+C0?ov-ua24mQ-|QIxMG{zd}3a)_la;!r45LDVZk=wFF~)iv6hT*xp|Vt}t4KAITWIen9*$29$U{pur6;xn1p>aB`$ z&xD7p&-q%MJAa_G{JBlH>w_e!bb&X|ez+Irya`abw*$HD7-8uq%&B&01M!2u4%f1+ z2d1c@aK0$Cn*Kw127?MsFhjL+eNrBoWYB3GD?#^B@De1cwkNM#k+xWFe0sHa*1X z4ThM9oduKmqN1XR8wSR#?KTAkA}}W*cZ9L$=N$!hPu6GHtcY1R=XGc(Ceu4ms_^aW z?)6H!>8Zv)-HNLtd?cp5<;ZFOzOeZ0lZ2F-*=-V?#=0vgWF~hvKYz=zV$UtB3ENN< zX7$P&Jvx+&=QPRNuTS3e+AllFgg6Z{JxAbyqoBnoPo(L7u5h0vNhU%7<{B|1%n@LE)x;sG` z1t8cZqb$*f14Fv@jz&MUfxvg+^ayjw0F3gI0Hj-%(BC+&1jw9F!B8Yd@AK(D1^wV@ zl(e6u-`7O#>#f_?1_L&igEBDHKXNx(U%LiJXnMI}mBt5b@Q!SGvD&TZM&@-Tw! zp!6+2jKJ1KHHq)Y6j#>!WImF8=+N1R*Q=(M;`&zJ-K|+GyTT-O;8J} z4~jV~x5bV*a%NH;J-LHlW9D%Bm9fv}VLf(68n2t=T1(y9UP>08G}v49qF%;iN_hvG zMD*YRiZ33h3vl29#puic+ynBdz?iKT9pkHoInV-4QObIPAEo0&9xWFaR6#=~OY>U@ zsdss{yRD)OF}$b+0s#0VLuW$JXgF#(6t^YUJptfOuwXD>#E zm4IyMkNPRQUHb;7YVvF@j3X4yOK_~ea4nOiZFJ)WzM-Kn+W*uG_G*r4KEY|Qt*m2?MotZyV zzn|*MSq}dikUIC`h(OK!3bi{Ed{T4$F5yR>=sL9y&J+jTo^cO3a#2+!9)=dnV+YJ( z0wd%s&B&F42%lyVa;1b^MP_Dc$1GlKa`E3uI28qQ%d0l2%lHv@6LK4AYyb8QC@L*w zA>9;ck%O=~!A>%Twi!`do1|Habe}I zwO~Jr5tyjdnf)8aC7!~;l5L2$kni3_2B<%~JpQ~C^ELb7w=U$qWDqTxJ1X$xg+4uB zE?Gy@te|waWr*PDQpCv)_Rm?^S}J6N5QgPHAE(Uo98!DYeM*S7j~;1_ zD)VZeI&~Fj@*tXGsGScXBk~exshDZgvV}K*7tf5kH2~eo_T9TxNB8oc1vh#%$Hk2} zWHw7*sh^#`DgJw|f=Gh&z{ut%(-2rHEYYv|M7-_v8!n5 z!CCwkqFJS{pN&f_wkUB(Xxu2a11Up@{^9J6gdzr!px!meXDvbKDcZSwHY9stq)93CbdKxk+z>5_23oe-`)Il` z2*62AqLY?07uT;>gaTUTz=6OBCCfwrTV!C0(hwxFUQV_Bwh>T&GYJ{5gEdZ4${Mcw zfkDN+)U2oKS%MDwIPt1|=a4!Ev+;BmCNq!BTD6(m+^vRJ9ydjs+niq&CBRLExlOX` z!}P3;bS3H1*S3VcMEqsMicLjEP*@VY7Ht{2AWfyNO5787XIt8n_WLn=cy$O`A z?9{SoFN#u?cye<7re;_3{hRVU(A=(SJZ%r(CK7~A#LE;Knwow!il*v4-0zin80@u( zYlQ-vinDW}+6P>HglI%w`rb&7>6saVWJ`3y)#uK3I?Y<7S--2X3!E){Zp&5O zgP$%l@G!1%>)l2gj^OzQ+Dq(jcA{+PPm)x*17lueIy*Y%5xP6X0XU?tFvv?e-{%r{ z=|25fi;D8rdo5{MqV)#5r!|k$J1_hI6rwgtym0a1)zs4q!hCU5yciOH;#m7YE#7)i zXZU4H3%#bMrd;iILeaxdZ6~2^KuTI*h5}>ml>7G;MwR^qcJ0DdA%pgP1(D$qX5G)9 zghhxIV`^GulG|HWS}IMZ_fQv+Y+F1t78aJv5gokHS(p>U9H^R`HL3U*R#aT%*&BU+ zDkAS}YmMpR=-KTIG+q36jmp(Wu(!USFRp%v zlMpgC+*#z5&WXwWiFL5rmF`?S`$u@L>>mC04m%u(Nm@HO8IFEW8252t4k|r1U7vdX zqI>?z*;f&6$s|BCz3a~H2VHaUo1xqxvVo9R!Ssm{D_wY+CfFF9QOv-Vp6z!Ne-Kq} zLWA7pGZhnG6&$?uk16mP83yuxytPgAbL^6;Y2TLfyBuWydl-N41gg+yQzZv=n;l_4+9o*;w;9Xh6F!O9kQx|DD^BU~XAT(XZNBPov_G0e`+ zre$YCWkaH$?rZHNq*4^VP}sBD+Hu*D2;q>BLay7dk-a7dZ3!#bv#mC_ul-O-*Wu#h zTa8if&r^sHfFhO>@ldB79AMB-U2f_&Tn%l|k^r<*$bKL`tZJM`LL)${NaHR5OT+gh+;U2^*w2976 zBfa^K8`~|Psvx@M8_nV{7sbs#ANAY1=o2L89m-R~o`2I$OMkP;V)B{$@}-8O1Rqfj z5DlSWi0WklfkQ`6uh_lf=~H&NYmiGpjTu!OiJ?l*-UqV{7|Kq03c^u>FphDbUkBLT z+iQTYPn<1)rO?rpV-Rx)pUYJR&CM5*?9mB@qn6(8!Oi)EdMdyVP;1U>$5|0ba zsz`t|-lpCsyq%W-=tP``6v)&FW)|5j>i22iNnpCFkS&U+OHvpEu_T0rW~T;4$Nn~B~jB@uQQvc#PsF+OmKpk)h^`B zO}>rqx)G(9>>n(cFi6nV=Dp);Kn99oXT` zCeqw!a3P=m%$BK5&fXMX_5L1ZWa$+k0y<4cph$v{Nr7rTDCU)xHo!>(%EJfH zR6Y$Xc_TZa9D|N-wBH3iar^b(V~_`nopVk;t5*k?EwUqD<+iaW4Gw?1;-~59pA%3# z?|lif2b)PX3w32VNK;yD zV#YJKvq$WH&CP8UzWNI^>;mHty3nDiEN5-pKcvn|qescy)ltA}gcUNsc5Kr<5)$Z_dXLQ6u0TMnrrR`!br`SaKAQ*A~QZhowv>wQmH)F~=E*+O599k(JOX-Y;zzFg? z%YcsInM9uDZ&-SvkjdQT38hcmn4gEA-NP6<;YOR3*txyx^#2WyV?yy=!zD1CDIHgL zAvIx2{9Q>P#wn5oy1KfifUN9F2_-K!)){&0$8gdl_{^W9+|k+yuISg#5AUsw_3IT% zXjZgPz>tEFJ(@-8)&U2{{z=nS;G!q)IuMkRX$)EkCM*Z$>+nXxaxiZ-` z)MPMzQUHY{AOIXqKu* z%C*hSxzQVimns@QLV0=@xg{82+RW<1d5f5aLE{F21HX`v5~LHvWDqw_5>VMpkApC@ z7c?x+>KNa24!R63Za61Yg60qyA#2Hx0812((wh8?gs?~2B+@24x*^p}A0vjF7Fyub z5Jk)QX)2AWS=neEb(EZ3(ck42Ota~2b{f|TiCHPmf^Wh-u{l~aoj)#AAM&4^);+)K z=@YHcN1h)hmLq=b4$D^ z&8|*K^CgErD3S?Oks*&K{*`lDRc3{mbbWY{L%fI z5nbf$>?#VF%M5|s-~H$e?bw+3v)sop(avV+g=qsxfFXYpcbji+*ET?y1U@HB9h_V_ zuJ4G|(}4qQP@S@fZ7fpk^IjF1#zrKF$Ydc83)0^A==e5EQ%4$uWKEpI2$s(ksf@Ym zeM)37$o!rt0A+tHPl}$^zcfgaf>b4;H^kLZoH252w6HsCBcd0yckmi>f_v&S?o(&R z4xPYyvPe+S6RoI$Ar32ls7HE6>y*X*$f9t+8_VDS+^|6OX_vXlu6g{`!q1R4*X?+K*B5NIx`uR5mS7Ov?`!^gBRQuG)lzmYwg;#m%(N*K#p`Z z`gp>WQ|7T1ZoQqGxivzPD@rdtw!()~jA52eRR3h>oxMH27MrK!PyF2BpY33?^LaA6 z>z%enCXw{$xjC&>-!h{8Kas`sf}#GqP1aZ%pYQnZ;UxW6%UvFq=s^xcTA9ulOdD49 zJ~uEs$hysP%OuK%{BD*n5h<(w84 z6?GI59zhJ8k(o)lGvM~<&gBJ}EI+Wpu^ekMBO4*wHC|H{!4Tu?+ZlOy>?2?{>QgDN z1K6V@cR}95^pu(1YeJ;R1qHEmu53F40D0z~XN$8!A3uJy>R?mMbOyv5E{)eMP8 z9|z60wrWxGiEh9q=UOJiA^U9>Jzi_Vk30QG*3-^>Hm9Tz7uXef;=!WgVe=Wst-I&O z5rRcN7VP8-;Ex^~I2$KnqgQYZUTB8PyZpP9yj0-=SdS!&q=ls(CRk`qTaaKC{;)YF zfN;t?GIUv3Ynl=c^qI-W?LpfIhQcKxwE6VuT-L}r9}D8=Br1y23=>jf3W(_U-DNk^ zPm+spvZzbkwR=;{u22O+f#<|~B|S3{e?54>n`BPkvNR68m1Wc2Pz32R!Q{1#jU zqJ!tgRnT8P!0_z|L}$ToO=ImZU>O5(BBqdF=N%T17?AP_DI^}LfboclKR_wFS)pzP z3w>NhB>Mw$ZLaQ?F*aG{Eup>R7y~39-QlNyzb}19&~;tLK1-I~MXr^?C{^FHYxSo& zR(7T9h8)dSILgMFj8?57yt;}mTw1a)_5S^>d&50Yq2B5)`*+#8IPcLCVV|%LQ!49w z{#tRtCVqH(Ccj?u?2(NxUY|=&{3k1_lR-K$5g}UJ;7fN6@ebp)6IvwfZXPq$N=ZnV zf(wa&@NnakM-PEa(5a7cVphi~G8RLLnukylWF3sgT&;UT9*!L3@8~Fq_XI||3OxK6 z6#RL!^k)O+Leq^>1A_obh|eAG0aykaBuo3nXl!=@dj zC)Da?`QxlP=^r;v1vj}ep}@LyR&K&+dkV`v>@Q0c5i)w1563ci zI4Z#-X5HNkyJKo-ami z3A#$Vkj^~(mCDEHmsPLN`JZMkG~G>aYI%vylDd0P$C%AERrh^xgSC!C#^nfds95W} zQ&AjslD>8bKB;i4Fs)Ru8$hq@%)aY}{^6lvn@VwVXtZnY)ejYEQI9AwvfDUhw!?W_ zz-yr}`2ekjMw6Q1Ntu()v&%SkNS#c3R~O(gMe}%l!5Zid+lF8$Ideb)eLDeL(3}}Y zN3N@_sZlf!Fa2%v45gXOw>s{$^mGvmL9P_@Wzi%Jaae`(`c_HXtwcXTdJ-158{3mR z$KRGofmummV)Cxr+FEoMeW_L&y!SL(4gt$b)k^gb7+igO&k@79nT!bSqjM zCZ^orHepb3IifWwaPDZkWZ|dVbKH-gu*$@1tKJ#2N9!rO`74bA*NkT@e{_g&%ZUg; z0v}K8auP0J8Mv?e!OW?mxdlISbm^o!)u_ZGsF9Ky3^Nj|3LOM^& zOWQP!>;?s**Ra)&>Im#04QOnYS|{C)M_>H^J9CHC;VDk|J*)QQ+b3ej2{4L>dNG4ob$Aa0Unc;XV8|YcJCb7tl)=aSXdW zvuE&!-4@ea9}bL=tr-AByBGc(5zTzgqv zZ6$M3RUmqZSTXspqCIU|^@G#eaE^^=$?b0F2)x9w1kb?_E)O4osF<0vMlwOoPV9PN zyt|ey@XN-bhaSNn#JXzCZ(M?K0VlalCsNMVx)#gj6>RRDb*t`Zr6$Y!-XohxeT~)8 z&F}Fwq_;*EZM(I?G)?_zvquCFUzd=eVmPmEJLGdJcVCv*I+4FpF6#{|*#xn#gIeB+ ze%I8eVN()5c_d#>>C$7fE6x9uJ9X=bQ4eO4ye3l8(y>_FzwmV=I^m5ql>l$9i-e&` zQv2f1(=)R!&ydno3yO@1>H6Wn8k}5Rzw(hntpT((_8)n|{+!16T8#>pUU+>(bQycmp?sr0-tpriNZJ|#E{BZt1PcP9P4ZSOPqZUT z@^6I*S2nVwRHTw^U3O~5=H}$RwLLEfXkrgr_t5Nd4WRm8wMs0Z{j8>hRWQW+3&#) zm{ecLlf-iFwqAl-R^ zjN=NrZ|P*k=J+4TQRV-4t*1?v!Bk-J&y2ckr@gIhHE!y!!0Blp`6xl)S2%z1C*&z# zzI^#9V{BlM;gki2rdm(Pala{BJo3ns^q~p_+JO`f4T&S<)PGQ7`ftlum^_^Omakw4-9!*JM-@zZLqjT*L?U>V$ z+99#ctmHs!#%q@ZNI%|H!b;QBwB?{l=iW=VlsTjWgBUJd` zvVSezck=X;V8#WbY~gC7wd$-1&mXAiX=dSYPr1YUe}_8;uq-yj*cTMmV$UxyekBtn zmaen%ap&4QI!s~J2S>p+{omJo|D^QP+S%G_=KM1eoB9Wx0t5x4UaQk*`qti5~AybI1SQ0i8em`X4g zJbA=xsx>3r@!`RMw@Y#BrRW8bV&K-`w!Df%2Rl4z{ zzO3YFu8fFDY=7Kt#F3I{u~ytHqT9TAGsw~@#uq{a1vy!TTMjSZ+-cd@RTM@)*@pVr zcW!O2XQAxRGIe;_6>KO!8YFovlu~-1;|vX%C2(g(LQxc&v>*8g-*|O@ac- zAD(9JUCU=9!u~b#{R$3dio@wE&kL|dYIR^mpf@BlmF|h&yULsWIB5#Eb4bhQ zV%DG!F(k+-@Y_5=nQF}nC&SF8&$gT?+lyU9HI|xYoMpJOLtmQV5;DUi zoLH}D$Afh8Lbl4H8a40qvx6asvp6DWrtX1T3r%So;=wqRQ`1(LD4CBH+2FV zpGILjdf=Mvs59mL<>8fiK>@>yTE&$deSNuNtEBalgKzFA-DmK;#y{RBi1+(vTZ+My z=1E2Q<>$^drjO?&(JAw_lG5>ft6hLRfE^lEQCvo0f|iYYm=>up|Jw^7+B>S+pkdOhRmo*%Bk ziRO4>*X6QzeHRUk2QJ5Ts4%wkJ7qqg2`o}U5vQp)aQO=L=2zo!8ZFJ2Ur}!!i__7N zOxNhC{P6C0YVuQ>rG;nrm3dZ9v4Qu=-e=mTIiKybU)sLs(JDAF0%sl`sMLiKpAx$V z6%J$1M($!4f4gq0km^55@MlWnKKC(l+8_Hp@9Bp+@%_g{1a~}MDtyfFqEm~?@R-Qm z?3a!=j}8iK0_R4SFNP{XlA+r$bk*^JX^YLYea)7fw_d!MUh!XpquF-?9D#2A#y2>F zrOugS!VZ=yaTq}=B?z^~xkbu{P|G8IsGv6%Rt7Slrx4eUS%YJUgefl8%iR~^T=kbM z@Ds*Mq#gn;3)>SU?a-5XJ3Pd&wMmfk3QFqJZ~*~KyjglZb7awujiWoOJ_*jU*t!QmR%(5ytei`n|`^C?zB6sGE^36Eol_jhNBZdkqz zN%sZWTSaH2N-8FtS87TTfS6QRsFPbHEe#?I;r8CT)$8CtIW*+&3)3^AB+!Eb{WPdR zBvlqcn)bQ}!B9r{&1p~s93364I`p~Z-_sC*a=R&F*UcaTAl8NeUlDQ@fgv8U7Z5xQ zCzm{j^IE@6F$PIY+>S{;30Jh`_5x58?t*lOcL#I!@_y3q0 zxP}R^S7#AOB=8v%#v4QeE-OYwy}sL*)G;fE%H;*p%Dy5SgH#sS^gr8id_xOh2J$`h zLem3uH?U+_o-M%;t8?s>LTrdmxpo}B+$cLvHIPeQVqy~s?h?8LaR~_;wy=iFIs$>9 zc!5n%&&WuHE-plYE6H4w_3Mim>Z`s*Dw(|_XPXY^4QO)nS;wp~J3fAoGA6q0G#Lh^ zEf^3}0(eVaFCps|PTQ3j+iss>?u+DB5=Ibk_B;OeYIMV3w!oW8FuZlfJ&uh zWcUM&1Z%4qqsw@f;gG9=fB8=aX_=Ywii*_s{ZIU$=*9_?l9+f&)t)L+nF?1qEFY`> zXK*Dvck{S4$N8|h!J@QNnZ25vhIxaF(TZyK-&I~xZ|U$3U9OJPyafaVmP=b&S~^h^ zR3#+$25dN1i-YDE`fOnL9OVoUFIZ6tVb0eEu{T>SNHz?X`nWgl$4M;l#kCr?ETrF_rNBRtL5efstrzJ0K!WUnF?kvqK$== zQ?-&(BAGvnKU=rzOzgd&ecgHfD{t_Hwg>Acq;SBNW^abg?dk2Yf7ExI1Bp*J-znW*&!i6tid-}o^1ul4#Gh#R=-hKD) zV>qJf&2F}-0yLvluLrFUtuy!c-DK;{wT!TXyIw0dK$L8#0d&Vd|RyaVN200^NJs)WZP z-9a~mPKbZ$1o0hLL?nI?ll01qcOR~0^Q+^*tPRRhek9@;kPChiITN?@z)&fYz;XMD zSD*YH3%$Q93;#=w)2PtFs!X?&Ic8PWSC_KW)e0=4m?gP*um7lL?0*I-%;X=FfW2#o z>=!pyQ{v3<$jFCCC3eu2SV_17N#lh#?U!RiIKJFrUOQdScP}v~r)h8o(IZ51g&`mH zJ#xH;q#a1hd826h>!_^Q-oBt0dSgQH)zLl)$_ec2Lyix^dzF|Kw0D9A`Hx}KaOK?N z)W4#Qu|ZGwW2ZZ={7&1X&Br2il}X3ydH8dOzku2#VdxSM08m-Co>@dQM1DzGJgPhZ zrohJ<6KA^6{^OD*LOrIB>BdELRLHY%fg zbvoc?5+iaNWIX=;G%%_!v*JO*;@m}>SazFgi)ge#2cL0V2(!G2)OmqfQCc_7x(k8- z4#o@T&p(G&CJg62+TQ2d1B=7F#&wV*flSgHJRt;)BAUWB-1I2N_?e*Y0AVYCO$1PQBVg2!(XaE5zzZ zbY5KiHR3*zU;D-5RJzU(R5S?g6;%{3K*t`4a;HcFtTSE}LW{u1;?yz|8DCE{f8!yV zr=5QeP7M$iSSaN?G2`vwI!`p`c6+*KGYFGoz=eu9@nP6W^o#t_y>=9dLNNcu!49t` zGMbgDkIfb_t6p<)VYUn3P)=#N_p)xYzt{ADL0taqOW5D@iKY^AUB$tRb6+jvdMFNa zMJA&DstGyMNxgynDmE(Zf6Jwgr&@0HUp@1f6! zHXngVAulifD)n7jB&2Lq$NvtM-tva$@p4Ls&mM`wB=UH2em?!DPoJ!9KnGBbTBhn^ zT{h7FZ&Btq>jAtP3FLlO%@KdMH9ShWAown*PM!=>`w3~Xlz&pRv+tp;+etgL((H#c0aHBX&dhcc7gScpF`Ds`^9 zw1N`udpq=N@D5Pjq7tFN_ao@_wQCzNa;gsZZMU+rIt*ey_TtiFZ)baZ<=@*5>%0=m z0|aB)f3rmqy4QPN{QQLru%w`Xh!Ip;PUz6!&Y*a*0F+M9l)%{;9=Nw5RN>ptpYtXI z+UHC9`Tvp`B0c}NibPI$(f6~xtlMSR>Umsl%;Gr9*BpB7bFL|04ZnZ$`CIn3I~~7w zkPTw2=g4Fb|1$sX9r^!o%Nn6u);%j2yt1NvSn#W)0q1k1=7ELpZNNHKP+(SeE^7d; z-mp4$4eLK_N*Ah4Ni!f^zf(! zP0lLoHX;U4Uz31kB$6<~)h63`CjDW895hs6U7ybHdkTKwvn4#ehz$qlC@?m_Byo#D zg9K_;!ib0CsI?2Pd3{4eHHz-cVqWA*;K`!O|p8d*(h*=RO27)=p>~())BxZ)bkv^ ze?)xd-mlgu8$(Ci&r>5TBjb`jI&U6*Ili|Zg`^hkQT{?WA2+PJf_i@ zsoGL9+0_bkUSI^zvB$p?&mc5sq$;PVFWj_gQvfQhirAh@ec^kF;0BJ889%Bej8IMx zxuW}6`oluUE+CWxpK4KFqgCJ7D69Mf%`89G!}*&H4(WRoh6!c*~{2k8l@IL zBf`EZ-g*Btf_Qf=TkRlX-p;xrirk)4TOA}M9` zBe)~gxX!*?yUsh1KnSocAemSvY69LPAP5r89Y5lZlfen0kpUk?0?~?}#(BBd*Z8O6 z1Yzaj8HicE=pb%Pum}mb_zbkO_e478;!>pc0j8M5%NOo@*<(K^dIA8;7G`j2-2}7+ zM&9w<+%{iiZymuRBz+@ZLO|nR|JA1d3T~cDoe9m<6nwdBd;>1jWV3-Bixse51#Dad zc$0iqw1Af*%r_!sXKB9c8F96NPMj8IbmV*y{8#K7u``0v%B%q~E1@YTnU4zM;zolY zT$g@+X*X#808zV2PJUK{cnH;Fn-@U?Ri^|WknrV-xOGCOo?HU6!Fs`?0AAh7Jgpf& z;8Tm)NPbh#qFIP#HcoL$EP?czQplQ#!4ZCtXYQ|oD_5=v+xPDU1juUf6p-%_%385H z;*N`EW7S{Md`Jk@#*aJv3ZsL=J~Fw}5n^7AUgaw^GDWYoN66O=Ii6HFzw%Xv5FmI>_I0a0)KMNrz6Yto9Ns1@aaAB! z3WK{#8YiGQ+{6Qz*y&>fy-1V|I8EFNtKHy2`T|2u%t-F%cOvMMDBB=&{aVL;ZM&f0 z@yy|esHulaMiRsnP6eRsx>pq&aW(zP6aCz27_uTgo9Wf-*M?b<$~;8k3?utcbdFKc z(X#0}iwhEM-&xvGNg`+{?Sz`V2L^~8l4^Pf8JuT+DwGq<0Cz~bZWvT1!KxCD*jG50hHZQxss)tl(Kvo#Z)vHmR*jZ zFzGsp6(<@BejWpAC5lVn&Owx)p3Q-zA!8(FTIOOR);RcU-5Ee3EO9B=HIPMOzIaQD zGb=5v5)I5(GJXM_KitpmyZ(W8 z(;xESB;@INep(3A7gWqOt{9w}LIviJltJ8p)*V^U3p!z`gCE$q{bX+j@#e<#|cIQHI&yvlX(ps{%)2t3P- zS@7bQ$D+{Ekd_?h9Wii_e`!lJB&c>W^1pn#ZBm2dHLik*fx#bUbtLT5IxY}yQSUs* zcaz^wdSn#^AZc>70uBS_Q)|=(an@I~qc*c1`w?$cW^a{=3T^*Xc}lS*Tuy$97c4wR z?`DpHK=)H`uP_Os0>aVz@qCHeNjOoG08HrWh7xQ*@T$!Tl?7S;m@vx&2_ zf>*=~^k2CmuL&MS0={}|padilKBT-w(^)iA{%bhSU%c?`uYFjM&H5Z0H=e$A$c=29 zv=2yvL|KKx>hSzr@uK2z+{?F)AM_lYT`R0EEA;NnJu2(;>D=DFADfrQK`rqJc_CAP z_b?2qB_U2Ea#~18eN-P^-;<@q-_wAyDTy^R^qzlxf;G!Dbz&}ki_P@6bS$O|j?bD* z5I>@jeLFfjHGjCj^KI(yF>a7qOukQ};FCU6 zeiO*Anp(uW8Q_vwC@W#MLxK(Dk}fH5$>OP_35rB*gfilt1h&UhO|NeW(7++tC$VEb zmZcYX0vw)6VD99>#HCj7kI}|{K~6QnM~7g7u4(9o3zI+km6v4j{A`zrgk)zg>^kAE zscXm$85oK$GvH6w<3mG31nK`ta~6Hy)_v!BP&d#9%B=von~Xa%GS;G*Ba%&wn=z7{ zHTZj(o%&BhrM#Wf$a#MZ_d$7dj=EhrXI|A3lE$MM*+DQ%DU0G?N}}59;un z!^Gc$xci^3S_yB0SZ6!3TaqJ&Jgd4u*~xt^n8dv?(hqj+)>$4P+ad#kaGbZImTk9Q zN3o7sRAIo~n(ct6j(H=FD$_G(QuFPhI2dhD-}G%}#xQFqBn@ac(R{XN-1W`R%d^Ul znG_hXVYn zjQnAA_|j5^=WLdF!@U^PMPe?3G2gpc_fx0t*8ECIP97W`P0Ps9N_p& zbz|sU;SFq#UUm?Xajuy|tPN2^CC05A(FbDh6N^yl0Xif2O#z=FIWzEO-pI&k(fn)q zG8(lP(B;NFi8*K5yC4j8VO%%NO3g$Nf=3z1*k5ML~DVsq$Kn18h zqW$_iQ43mSF-urS>0LnRJj$JwI11XIJjkK5>qR&QXs;$F4(;oLNJ4LXY!AgCKttUs&Xc5`FQT)noD91q_-6jsEHj?ZSVh>v6J`X@G-NaS6iZ3!w=L@=l^69 zF_ULhpqGtZqdZm7@KZ_el*seDV&MVb{&c=FFm3Yr>2X~)=KaJ>5S**U#lKIR*VNRE zzN^?zG)k!cl2Iu1hRk37_U(KXClA~}aKwau=_#@~Q5B1$wq-%*)8Js@>;>^NNG$iC zGCZQB#0_bq`3OQ$+Vq8vzye!fZ?oRO2n-Db|t{~YlRtKQ)3VRs&_?3xPSiJ^h^gEdpuzG&|( zmu7q})cXBf;WQrm#sv$}9q4nmGBKmxoU z5`}@86jDbabFFG-P@uu_mrR`4EJdQ zRaJ4{wL2sFrl#*VaddTbguX)FNLtIb@aa>qws;9C0k$mFI3{7NoOI{pEG+In&%K>& zDn*f&lfyK77hVx=Xc*{;FI`C zkzqrdlxwr|B)LDhR9o8G5J4aTrEn?;=H#iQD)NI z_))I5o)~&X+60tal^MF3Us0l2-yb;?+T&r|Qza}iYu1#RGkmbQbE|L7lFo^Te;W7d zat;q`f=sS{@h*6IXxge=UdcGbzPb=kZtwh#&d%htG-cCpKfe_Y^G6L{{QJ&e+{lA5 zhcbJ0$*F$FdceMWVG1}poR!jA+CQZCp(vD;AwvlnDk4*eWcc5U+F$4I zI@dnu?7ctU_j!K9TKBX*e~FL#QG=Hw%m+D9MEqX*a({n(MMQt|#0cHJQ^yq}G+Sx| zS`y-PPoLg+@Ae_=5t`IOStdYFY~#C7Q(+$@_3$;>+uKjI8_j$dDALx^(Gl1t>IL?| z6i2k}fY#B)J9oVpo{u~QbDTiD8}1{by1-5J3ZnQ8dP_N4j`R`uu&h$i%sB8Cm1(>rw^Z{8hCm+lFg zSF}?vW|(hzQvCw|Z_lr9+$>V3WN+EnwJdI%myg+kR}vG58GfTy?@qnIbMbjIu2^2) z$YO85R3rFwbf(yg9?yLnJ9sD-Q!Zug0&X518z+ z$W|j(sd6pt)vt#0eyov_@`CKXVvUZ7<=q6-dA##$><)mp;Eey((vlPxHv@=tZi|(r zWmN0wbj0N423s{|*3a7@GCm%{W)2Gl_IvAG3l0`re|A$71#Ycbqh+QEeY#MjfUWUk z*;AVr@EJc%8s2HNF-E9~|L5uBx3(Ioi})>cR)70ps=wY=;n(95?D1FoMn${o=*uwI z&2#M?lNZjkU|PCm?QAK1;N&2f5A^l^go}#C*duZFGE^Txq6DD1g1REZ{kJqKgKKR( zC@OkROYhpX3lp6MG4MH;7p5*uhPfePicdOyj ztFKf?+%e^%cA8DJXen~?`fpno-!d-^or#Gc?)H6s>)%$?_RObm84(WJ{2p{bZSz$7 z7Iq)0(zU^ny|Qrom5HPK=)_*2X9_LhN}1#LD>xbstkZ3qrdqOO4bM$X#LWBf!SJ^k z7l;I9Oo@hkk6-aR#@wJ=7lgYFJ9iU6nLK_(SXXGI!$W0;x5;V;KbE-dT}{kHl9be` zklFIY$;pYd56C0U$qh#6#CObgUu#)xon>lU12s;bfII`1>a|9X4vthbqtH}wb6>AR=eg?P2dChZjnZ@Ci^!arxuoCv>-n%)>g70Rip16#~snt+y5#CT|Z zUpyvPg_>#-@d(%b_nNdB_jiOA!0S#X{H(t6t=0f9gd&8)txn%aNi4EUs-L0coRI3W zd-<}9YA0j5`M4$^0D3Swf%-?7rtZ&sGS%j)Uope}y9o9}>#_aBm85_z$_+*bopuE3 zb9UGSVZrrjl|l`U5NDxFe%}AyvV;8931q1 zyEzt-BDYQ7pb&(_DDj63TIU{o@8@scXjZ(nX#)bPwsYrY(>lb(@9b(e4{68!4bA_Y zcq9Q80NLQKYHe**S17dv^ks1G`@w+mHLl$}emw_}#o%&qaBOlEy3CA|BeJ9n(l$NT7tn)Pa5+ba)U)c?y& z^$xMv^5LmE-=qY46VI@ago-uZAzP0gJ?be1IJjVJv>l1Mj=8r`+oif zNWIM7@Mxr04vSgOV3)4$IHqwV)7Y#o+3dOM2w$Y8`MHz+Fax5uQd1|EvqCcP`gK7? z#l77v8*F6WoV*qrM*^Liz+Ht<&-Shi$| zRhO#&QSrrBV-#-xH+x%WQD)X$QJE)^ev_!hYyciA692;ltXFtwxbs zv{+zSpSRkSQT*mjXe?zBfQce>lyV)hym?>q;x$tg9ZDbK9#KO8t0asO!iPrub$EqO zn}$-v1{lYffuOK30jPd1prKy&{Q&}NNy1LlMtb_mruQ`bti~>>5fz}PA(pG4!99Ci zpj`2<-o7oM5$uPX)o|=8oRq^T8=RVF@aL#UdlQ7yj>aqkcjl_1&|+k8#^eCY;;7|# zgj|Z`H1{9p1c8<>TfST;<85^{5Ari(JGT8Y2T3?e?)AZgZ`s&rIPvIqDnk9pN^|52Q7`O%cH~eiR8>}44ND(uCtCp%D=@&jp|%_^R`e!22kC!awoidv`Ey zJ`ezsuC=JPp~>-R>%jnP5P$13-h!|Cvfv6D3d%3QjKXJF50of_s~cKYg+zGwH(s2( zf{uPl4_x|ooda4hK<2*$nk+B|Z zyJ&n5E+2FUQ;yrb#&&G@@+C6a5|}HEK-X2Eln5*mY|CYC5D3#NF|)E_L69{Vg=WAL zkX*&O+<*Sm7)Nh?9dqxGA0fQUOPQINn2fXCxY1!BN{+$@LRy3x2vAc+3kwTG*UpC5 zo%D>b%^?ZK6VCbEH1pAw$1!Vy%j7fLu*}Pyp9ZoekpD#U5Df0e#a%=<@f@H9JHMO~ zRs?!7@}lDAQBRnuS}{yAVyqr*gXYG@(=v7w~nSg3M zpTESC4tEK7^XgSD`k-WtFQb`XM!h@W)ThB`5Pg<4lrZ67+63s1`{zyhm(_)*14!c; z#amfkD=3skT==u5aP&2B8vteOP11pGcgDxQ2{q0lIO-M`sj4AB+{+(;|gG+1aMu+vip$}71 zFm99;17r-pwIura%_3A5N0l2(Hy09NxpAPzCW~N~fMl7>SSaT@8>DbpLoW{lQn(&q zVrFPrH1@lhIGi8v>bXI(vsM9fR8AO0!GxJka;O#fKOIKp$O_N_%1$)wUN~G)b(TJM zwj+rv#0jk*AFaA{>5`6ui#x~}Y|Ya!c?jU3gF@85OWESUftjGcgKpirhr^#{BLJ#k z#g&gL#aeQ+g(zkxqFbS(|~t9eU$dKH@KCY)_R zUc7_PfX}o5&dz$i1UZFtlOs?|SEmF)l%n5R7~l#$Y=bnfViO>t{5;AdosQFq;QiuF zvU$lXa%@@R1H@@iq`y#hTU#5&0$t+UFqdF_lt~NrGfdv$Y3q!}3=V+u5sn5r)b0=j#8%G5ta?ZaZEktS1JY!0aJ*~3TS z5p>#tN+6;H_n%~N53YHlowZYGSgvWJ)SNcZ^@&D=YuoYG6l8*S z!U$d58u*|VxL>fVR53Ozdc&v6!iowmDnIc#DV8s$qInhS_&l)M4j9UvYhOS1s;X+P z%DI`jK&WUk4>)Os!?nnzi{bGgqY-F>Y-ayzm!y;fS2lF@L?o zVqjUGNYU2)(k{+x4zCn8&-6~U4?&1xp$rS#VaFwc+Ar{2K#ABnxO{wyUP`}D>DJCw z3^Hs)o5T{bj7Lf%924%`%S0B-LUIh^)`kvzE8pf+APIC?CM?jGzMZ@+_>py$s1-lywH}*C~WgsQr)=4|%g< zEp5pV75cBC+X7J#SGC|Wxs(7CKwy0yei{p3LGP=BZ3l`YLTcIX#K!!R732(QM-*OD%9YkR> zljkMoo^>tQ`v=bLdT24q>_9A?L#b!$#Ip!xo$+I*Ee&iZU|xAobgxL2Pev@i4`zC8{=Qy@FG zU6R#0Colknmk$|PsPN!wjQQbrXeJ}NuWG{p2zgYZ-d(IVH&wH%wi zgx4$GGG6&SGUyer#=XZc;`c_3%O@vlsgi(*?|j;uPhE6h%;@2%<_xd}SV=B@s7{xq zy@l2gf0S%T7!FiXgpa@I?q0<=Y{@u==R$o;+GVM#UGez-Slrsxndc!3%%S1gq`r=i`WEiLLEEO;Ra3~V*Z?g_n2xnoY*5qY5hRBDGO}u9 zKeTTd=nF`$5m+^r|Ni|vBARh(H*|I;9$92>l;I$N{WlpNftY)z~a z=JSNnm_SXZ3Z5!27@sh%e;`v3)f2_cZmPa@>lUPxh8>}6Zr!4>-GYM3ZS%r$d>h5I z3!^V-&D+`I1y>k8Bg*wkSjw4?*JVb*t1@@ENr}D^@>u zg|+hO*vk5wlgD!Qs)$_eJU*zEYjaIc;Z%&-?tcKkvfi*%35d_jyV5@&P}Btcqj>?> zJ?e_EWVOmWzQD8}U{TQk2P#vcj^WKxarft!UkwJDBw?#1BqlTF&7VKrvbV4AK(~RZ z>0(qn#C%fzA|`B5b}j%j)sb11&z><5+D7OIIHo|}3x^#XW*2uhH3>dm#K?H^gzU^z z=F;#GP!fVTEFA7Uc(8_SAvoR{ z@7=qIM!{3c6||q|QpMam0q?O^wSKed)UJcelbk)c*Ok@Pr$HYc=3o_oIzRXI>lxT< zr7(opnm9Q-lQ^1Gl&tMPJL|*;4;@ig_io6vd=ngbK(!3nqDu_$fEO({d^iQSt4EVj zGOBK5*)||Qt`G~9s-N7(EGw_X#Ff-ZKr3X9jtLa?vEf7J5CWqMg_eN{+Ri4*`cYFm zXIg^5Ltb~j9-h$dj0LNF&c4$-`DI0N!g zBuH`ZSwZn8IF%#(%B=VO`0A7%J*p(lH;NHS?TUI#Z$=5aLVcxcHJS-BKeVrA8$_F4-NF`8|3?{}f|#C;0n;w;!E z05N6<5Bi}}lg~NzQ1tc7mr76}Bl?hr&2!D2OY0C=B!cBP0wAH}tTdKO>as|Spr;6W zZqb;PsVM^uTu^XuIBHhPI|jX>?LIcduk!*(M4|s8kEREx7*M1`R5}|}7$G}n&YV#@ zpVf91STJNy7-PdkzOdJ?&m<;xHn+IpP=x9I)6zr>S3UU?gR?2cS4@oXAjrk|$Ce&- zz2>bR0Dd#$@nf-t-7e0~y6e~PH87Y4?~j*?Y5&NEA1=Tecoycwpnn_q6oj6^sl7_G zv4#o=fDQN&U4)o>N06qhHdpT1bEVPYY+yA&zkkvrOOX& zi8RS%P5~dH!CC0d=D4q&39oN&?!avLU+uQx%=+!%S?&K|4 zL8nt=^+5;}X(%37h{}!~>;QnLO`8@y;0jF#(a8V;M8ChoKt-=7tcgZxvGtA5hJKJT z4b8=70Q0m|029CpOp0I9nNB(WC)L-}xnxG?IOQemiC4&Z=&PQt?(|s}H-mznm6mqL z6z11wZ{NI`rS7rw^UDG%rP;?_Xp5JZ&w*K(wm7~gx(h!@83-Ru{shSj*Q``T5NSIE zy#bj8txDF^Iw0T9GmsD8 zE(smHo?DwAJ$yAds73Xbw7n`@$|R4`+t~*`NYH&d2>D-}gTV|2t+&`N#a95eq0S?b z3L3MIrgt!H6>kDBKx)6a^m#J3Bma(G8PA|WjnHkT z*YEL{mzQ6IdzJDnq26TujiI_Rw;K&#OfbP5#>U1Wt{fDtnafj()D0=%%Sjc-=(fVF z99rDC!A{|TXb1-J4_U_agFYc}D}m*3a|tH38m*1QN9pNmIyzhs-uG6lfr<`w91r}@ zlNb~)5B2>stcnE-b&z$_*gQr~4YDS84 zSu5hXKIK#IGVv2F;Lp1KUaG{V@|m&{?x`uq4a@%w{?nxBEbdvUlb+!^=Ur$$W7aIa zj4F5(P-nyzaOxNsEHiLKJ`c^=#lIDPJ_ubO4a19D&~?)s8xV(c0}K^xRX~TMAjw(7{1^2j5p7-3)QSHKsjeDgQzb~3MFS>HltPL9)tB=BxMz#|0e*TnOi>e0Y01Jk{s&>`>%yR8(?Twn2v*sOCfpM@y);(F#(u&J@M{@y(nze%F2@^ zBqZ8KWRaIMz zu>dA*p;!qINh-OAvnrPtu{~uYFDgu)9MyDE)P=H;>_G&pw(5|t~^Y?m$)Vr$<9Zw%Hl8aP;m{yl-# zw`bcGgz(_$c}k)B3jghl0lvvmC_KS=gE!=;(+6HL7pwV=&(Q%H41Ez4OrxUW(}`X- zq7%^$KY#gh8agI22BKV8jp2vOjZ&xWJ<&52+L5py$Bg&v+0=0t8hsK!nnN>!(UE6C zvXQhLod_SLEMXm@1JQk3b`2jT44u8F^P8T0d7JAuE+*E<$J+VM<{!JlTpryrde2vT z^FNpUPUR}SakIspBPYDUSa+ie zU6^E`f;o;*s#kB`aL2C!16Nmq=B1+lf$tjYsBN8JPyIOsit){%@TAZP)F0!cqJVyJ zaC0xo=zkoGo|`=6k=2x#k5-qcn;uAr^RQP)0K5LMAObjtYwg{aTgDBd0aa61E=f7q zQLza6rR1O+QstZZrtU9$xgv9=^Tcd1dQEQUK6CBewDH@D7>j550z1*^kN;@;m3A8f zl)%OObFdlJi$CY0X@9iyrV-iM^k;rS;g?FYz}|r$32dy1ctqLnyl<;Bw}czuy>NyKklqYnNy+ zZ~B?+e5DwR3Hg(h3<}lK>S}$16c~vM#}>_*6P|n&T>}(B73$J!##wO$6oa6AXHXIx z%uCWTV5njPfGevF_%{*Hir1~9tLtO(4rB>3JQ``ILZo#81vhJF#lUjHrg7SWO%8hy zg2EP~k!-U!_*Pa_P=BJq#s#0G8E!Bd>rUxY4Uom#rEc6Q>iug22^wNT%ySA+EMIGj zVddlVa~R0sMWvAcHc z!4928t9ahbOnvo5Hp9u~p_*Hakj!x&+qQ^b$HzzgjbG0pE2PpQ)n9!O8n2#}3%1(g z-wwHZ|2_*&W3Msi@9(wRf}l3q5AP~K?Mr#VIL9#T9Q|h#FdkMr?4P(-uHiJ89*i(5 z2xZWElI1Dr@hP>fsC|LmU3-+?=()8FU_v-U4s|HCso=M@6{BV!oXz58%ccRM^*VD# z<=$ue zU8VYVfg_n&T5o$@P`h|a6@8D=g9p3q__nKO5{3PSc016L2&hpQ?pFE!;a=@3Yqps) zOB45OAU!==Wpq7-Xv`0r`aoglKcPtzqxp#1>PM zzma9Ghy+()%$TK;b)VCvjl-edF=-`b+*1sFj?%R+}B2$nCN5 z%F&ml^@p!R{fXDi56uv&)ov%+C?33oHtcWHy_2Ns)W^2-@4 z;FyY0piS>~DCUz72t@ns4KA}Fe;`y2Y@%)(DAbcQY(^}>{?h4mXl=aqu47<+<$DCi zK!F|6FQ@4XA2|)PA=eT@fhbiEr^Pv3%6PhQ6}_;34`Drb~^;NBQ&xDsh zS^xnBv{1WINHW_2f4hjKx5!=meQQurOkUo_QiyJD%yM_LQascrt07XO5T_9Vq z{8@j34OTHRso%BXgwK2p7=?8dvaTGg&x&z)Hk$$C%dIb~KYoHgC>I(O)Smu}kn3E4 zaWc3Cyzrikk?e^l<(Yl(Qx03E@#x?&6NqE+%)RU&CF}<1CFNr#trUDZUBP|G6!HpI zyK9+c&<&rBcGPaR<1IOYGe6w&0HActZ_+g&D+i3nG>UNA%$aBJ-*4f3ejxZOGz8$I z1*N386-WBehyS9SZn!L&s(fMypE_n4ShT}?U@7vd#^ewXAZZ0V` zSf#t4dykUVv3Tgd{{BQy7Z>waIa;TnM2VtullfOb@ zrFZ=9HtrQhsn2Kv5ZP9tY(=$6M*`9F6hnX)$x|1Hx(TR}m{ow=-4(FTW6Ciry}gEx zj=J@yTQjQ??tEpgNrWo*p~hC<{$0A`#mmG4!&B4ZynZ>8ao^1A_*b^#S3X&Xa{y z1bvC(D8?gbB)eFgWCN-5R9s%n zPI1l(TJbpT8)|`5#)*SG-!S~#Gc3$ZXi0d>%^MfLn#Af>&TG{8K82pA`B%M)SGu1f z1OBc~?~uJmj#pCMhsz=}E=SSAt6~gwQ$~FtHnjIg4{A5JT<5-~~Tjb1v_{IyQULzKD_>Zc> z`g)V(uPEqg$XBg%Ema+>6=CByvy!mxNsfB+EMCXd8XA4n|?Tgz|Aboh=G3*_@TESm>Hw>E+jb`@VI{JtnE@ zZJW3&Z>pCl0(+whbo|HG6Md-KG2L?&Yf8%wCszCkb`F^zJ1bHKxmS@lOv)X2dq~az z84chB29DJN0?k0F3JM4``rL`etOt^sK@fNsidN6+Uz(u`+4Y6a6G~&P4>U~^OfpRq zg1#Yl!cG-YwfN&+3^SZ+2MU1t0fJtzBMHGYGZTQ9jSf{Bs6)IjS7*+^^4P@00$_4b zKr&%rQx}6JsdD7V56*SDNwKjcmmz~H1W-G>AHVfbSV|5MkkwvxAkxWnOgAQ=%o>SZrUu=8rgP2 zy)%B5`NIv<2wehbe}gkpI5;Rc%SZ;2l4K0oJb=tj5HE9~mspmeIrM3IoebtfvS27# zKR>7AeSe&$jgXtr{MkEO_CPX8TrO_=Rj%u}(b}UNeiCq{toLn&`ng#voMwlH-88Fu zv01)n@e}cyni*X$qBVj2gr)gDjrhkK@@>n@n&r+@3~v2Am1e{Kw-%RQYjX0}etP1# z12qz;DTl%~?V5%RAS(nX$X6xp9UEK&;tz~_q{1qQ@2IV1;~xOIh`}lqMX`y{l7xf9 zqZv_ok5L$b0lpWYEhUeV9b>onuqg)y*Pw%j@O1htsnI>a#oI?5`|t~(BI8OVIW65G zh-Oef;lNGF@Q`tOI-|yUj+~4TH{;#aj&0w*q3}HU40S^2JhAu^uxzAX34GfN^=jCT zPzAAsCH;pd#8y63Ehm2^>o1C$P~KP#T=-}zq4kgY!Txzn2MRZIP?JG$dxA})@IGM? z5ov=^XlOwi0uRwJ`vgqjS$X-xdJBM4vln_3qzc6{{)ZXfYPb+MQQHlE8XG-N-~k%@ zxuc)k4%4JL2;b>QqW|WBy2so^0sxA{-#&+md)h%Bb_R~S^yoWZyu$HvHP`e+w*F00q6+kKc@YctG+N8fw-Y{@2w`yBsPGNHImdQ>uQEWM$2DHdtEeHd zoT2&SjlU`5XnCgkEpmz#eyO%iD~W(-J@VX#zP`S0z!+7(rO2qphTo!DupN@?rRSgY*jsYi9Y2-1dfN$IeA}sCJXN?#Q_&-2C03D|6T)G4SQEUWZF0poP z+VwaDA>nExyH>J8qcTL7&)|GR1C1V!G7N#|!P>Dh_7lSJdF8&gwtAy}pasr)9C;oM z2=0y^2nyO$-?j@rHe84epn$~^SQwxZhg%fKw2sMZxkG)zaU{mbVkFPpq(EH7^KR1a zmr|1#?xx-YB_ zD%sz0QW>9IKVF|#I8{{X3JN%;;JPR{Dmb%G=gDFI@rqK*QQDzF34Gjz#2dCQ(vVz( zC_L&!`g8gSxMw$2mOwH9@JM$^4x}NC^a4*c3jkYW_1HVm2Sn&cR-*uJiTTxm3* zH8K%Vz9nXefzNRIHmBf{)(Kyv2&W_?YN@qN%)>nhzy{j8HTc`Q77F{ZkLWBf7_zCM zHXpSLQXn^Wn9$#QxNkaW@`N8EtPwI38k{RZNTWccPt4IpIIGzr%CcZ^^{Kgj42M1M zvD~sCTnMuvMFqd0!RHmCVzM%rJZYt^^Le{s;)~i2M0_3nKbPrb{?FexZF0W;|6QhA z5MKfX#avxGTU(macHo=)4nq$GC0pl8pXrRYB*hqT-EG5Q5& zF4De)x4FB!W9TXyO;zCixTOdJ4oGB$k3vIb~Vi!zhfY&LEAKi7#wKM_9d)J>F zUJo@+cwcFRn98wOP@UstAbxjD->(I{yn?X%q6dR~=8I#m+{vm}pJO|BsnZ_21T`kavSQZz@*hIy?J&!yfR_W51Y zVGP3$PR1*9AhOtz{1ysHVAEouOA#%DTaeWbV3%l{Q5t%5HeIG=5nFJ5OXE6l$Io2Cwb8r%#Nl96Y+HH~IQnVYYckU^m`k=HP!iOgEx$ixG%nSO; z`csZPfr30Hu8<4NbZyGK6A*A35>c=0y4qS(IE$R3z_BlenGfsdCDMYR1BOg(T$|9+ z*2ao`37}YM@7}Q(emo+uh&J5|Vc-rw|7$b`HwwkSk@W@RWGM$49VzHrvV=ke4yFc4 z&EBXEC?*7x%Vq#MtW;>?Y8Uh{^Ud!6yPv>4p?s2-z3O zKa-w(r0^&l(eJa5KPqCfr|*iSchocIz~*0+^-#g5rQG5Zh=vaD67F?@ivAgLK+XWZ z6+MwVv?F#_sxPk;E229nnMF1GfjNZB9>~8Q?0(L!>-J!C#3g~(8V-%ypRf9^%Td}T zj(ojHwd1ko*Q%Z02OazOz8Ai-*NwAK5a$t-Nr%Gb>Z(Os{;LL}4JRDn0j%`>0DO2! zL12imlJn)+7cX9X0AUoF$tZ|-)294p3(x|sRS647wQln+DmOp>OV0x+L2k<**E8!v z?G%m%memfmKOH)dJM+^bhfxW?y}E%<_j^Z2BVY_Xtv@xiFu=@fgO}QG0FA~UZh&kM zT;_PWec=N9ErT!a&}8Dtc&X&kL#O5eR`zuIAF(ilM)xh;aC9b$Cd$V^8-QaGdtnMi zyFfevm|m~lPfWGQAmhQpPki=+E0trD*X$^Kl;m>8Z`<&*XlW;>+vgSdJg)z{yIVAd z7YgsQ50E^wE$SkpOE_p=dVnO$-_5V(IN_@f7fHm0Nwf+S_E6C5llgq)SM!QW#Qs6r zv+0oLO78vWm4kydtzuVqotN+(*dupXBFp%#bykvulk=rDVbv=0T0f`<{|!a;_U7HI ztEuqw{3A8ZJ6}JI>8OdfubJ0&SoJS#faB#&fM3NVYKGO<07xX^e>pVafcs#(QiIr_ z6pKtmu~@B74({%_z?_zQR@RY861MF{0YPcwpyT){rK&1?`fRc06;kf96lVUzz}Q~@ z>z=MqJ(m9Nbk1DMnu8*PVbR4Vy}p55H+q;pM_>KZa?=x$5|e3KyH1^Nvf9ur>-}r> z2-p1c^X3l~fSPleko){vBb$@q2~kxH9*ZbEf`=ExJDas~=l|^(@Vm5xG)myu`{7Cq zN26}*%f3jz@wj~Yg%!*Hvd+=d+dIR(R@5+V`-e=*G>!_aFYZpR|j@x4ZXHmWBO@3T(H3-;S!$ zwc9BR0;Mvw3zhC)AM^Cvw)fw67aTIN&b(9eF+L5+Xgdv>+uCZ>ZM=1=>o)Pk$T%B) zb6;sYck+-s+nzhN34J``E1OwJq4Rf=0lr`AQibNtF+xh4El>IR_5Sn6E584EnIXGz z-5J172n4dC3cvXL^R%@NdIn!lb!Xftt#%L98@d>{>%`Q|CDmqjUxjRD{XroR?3DGi ze(b8z?k7I0%U!Phw<5Jt?h83HCjM*M@lP%$>irJ&3685AHP4Uo5OSXK-!JfdViOnX z$6&NpG_CjxbJ9x5_!EMpb@hJ7>Gby%qpp|hrrLLOS@+8+Yaj6}%dR^T#G8Kg?}<>B z0@c(a_wAYsyDLhHWR}V_EBKWB?r&RkWbdc00mq8Uc<>E*})9!!gs;rX6aNzv|$u=j1nBCIb=AoqkV%k*fS0&GN=AU5}5qdv;Emx1rP>FSAt6l52pcxaVq56V75LBRa zDTJOE0cyMMr6HJ|vV};Hhuh>is8sU@gOQJ6iN!p)7_lP>V9t?I5?{~`2D1$89ca8) z36<~OJx5Jn2CTnE^~76>jDoT&@+T@Rr1sIb=mCvvI0*nR9 zWBuPUxbD}xbl$EhfhN3CJnp5MQ^DLY3#A=}3pL|6{}*WO?LB(_Z!EO4(r&4Bse#uP zG&f~)w6=_-)V$rPdThVK==M)DcN7|WPH5Z8MJU`AX^R+dY}hVO3Ku|P_^eC})N>@o zp$iPUjFL)AD=YCpeM4=Vj;LK-7@r399cy(LP)jHgV=E0y#K64`wS523kK2}+K>?GD zQ-IJ+5upbWKC-v8+ZO7^A?)jA(hc&vhvE=u>sj0ZamU?%>8c*ag3C_ z_OBn;1_%l!ysO$2{&TJ8K~2me8-5cou&XLe!$<@Zg6St0W9bOPLxI0D_GX7JgyMum zkv1aoH>>FC)jJdVl`~KL8uuEJSq|?LbCfXm$zZ>(D1SsEk1R8TBlv?R_kX7}hx{4N zag@N@yN#hKnHst8v5-U=Pl#_x+{kjn3q@Br#;nA$Y9|nK zie=?7($51O3}(sUmejAk$G2?dRt)C-{^oaXadIY2t^f1WI{$CnaV(RO^mU~jEX(3{ zcFAm9w{&_vVs-|uJoUQRVa>?faY;z~Jn#=1zbs(~k?I-fyL6Ac!##qk`&_Ynu(bUJ zieD8I!j_rd0``g1bJaF4S1t)ja9fD3|L8U@nQzPk|#*n(!=;b z`z~ng5qFxxIQ3-23CUe_j^St5qmt3TS^$o+8UKsX7W~!c#9&|Azr*GUhdjgZ*DPDb z%^|nLH>kgs5&H9HeYYZW72VVOVm&6B@`YX`q3R$a5VT`-@_j1AQGgQ|AsrfRKN zoRfp}iG}qYdLuf^G<~-HV*g&ee@F883X44i*3K7$M7(Tq9{3BWoVyDGmqga^)iY?= zH68&lLJ^uNNBTp=%Cuct5skNr(0^nlTbP@pC7%qT7x#U)ker-RM=LHauDe-T3%$I& zAbVB;8tu#5PNoGs-aLqG`3}d-LUUo3!pmV3`SSDFG`>lU(&A1bZ`C%Bo%1H1EdI4} z=HIx^Hg9Q7Nyp~=y^ji;?!5i9akF+*KyU2TEADeXcs(*Rt5qvqIDYL0UxBRx(z@3P zV8Q@JZ=j|@fA_m|``rjsn*x(e0UCtEkRVbFXJg~TrIBzn!Nh9RSd7SxXMlV5RYCMQ z+c5i?(@+wJhSpm`m~9jLS2aRJ&QDc?Yap&s#wa+j!l@LNVA2AnMB?( zr9}dPNQ&q7QI!h*xzF5O@KFKubM~AOV6zWn%2VV|if#v##)2tq+>x;|KzM1I4Fs8F zMCDgFvCKsi9o`)qYZ_~ezPNPFlwAm%0=0_9r5Tq4W-6;r7$oij9|6Yv2Ra1bpo-4F z;rsXHPT%iCL4O#2FzQmMlf*v*u^BjD=w>IuJ3vZj0Ok~vhtohZ87yl!PZo@MiFQe$oHUsH}AE zfHh0a`c}NZ3KQ zMOSG@Kl}yM)xm`s65CuEgndNo1mx%I1oyty0OmP1kKo55(LtbLFa4&rikKqF%!rMUh)prqy_YQUi!%BVtEfd~2gKqcmDeC<}R__%M_pyHDC!+NPXB<10-Ly-yav zgR^x~U<^YKMS2g?N?*Hi<8{I(;LN9%vR$GKTi||nayJiw_6|Z6WN|QFnkfWojF4dk z>V=ab6~Nf*?1wak_8i6ngMWDd$)==zO{&^W?`X@3`@z3-4TG^_p#mpje*gaHo|(9z z$9iG=Ad8pf8;{Z6MFgnP^Dn=*nf>OvUyL0GpWVbLs&jKVL) zTvWTG&GYN_8D*Z4tCbjA~PJVMBL+{h=7r@hdIWTalkLud1A92R5hQEbuF=<8^uH+B*pas;1 z2+;I0Qm}}Njm-mc=MR=jysyOokP%?7^RORTK!!!AVbNNSGEUs=;TmGU8&3zDR^MH> zT=T@g$3W%ZLQadX90frD1{bbB2_*;eCc9U6EKNNUJcG~22Wv9*0!9v;IN@qB7Y&`e zwr=G6<0Fs&aA7#}0_e;GWlNy?#AU6~ArOHIsNu){51&%#^dshqSNecAI(XZ5;9*F- zVFhifTNu9vGJmhO!S2Z&Br`Ata*kM!ogYGQ$nn~l$~m`jL6N1`k(v?=K&$|CjyI?^ zBEukA5K|wF_>cr52UVPLfP<-GUk-toF@%&>JHgTY?7>g+#mNQsF$ zTBi;0xBB+&*A3Sqw>hvbOG&lwvCTVG&olZ(Qb($MTOVD%UdMHGu7};{thQ^q`-Ehh z^(7yTss1_g+bnHtVs_R6@*rFoR1JC4A(+&0l;}WmhjgYCT+~P$&87HMD5Z3E>l+&S z0KaWO?xW8B7e+{^(TaYHoPsx7qNA{-&w&3Gt;HB|I%%;V@5OIRyBc`3v(^Wg8>s)i zOLl!7SM?78QY4c(@N`|-865JfWJh?e>3&AR$974s(R7W{Kew_l)A+%p9to)Y4A#Z# z`1xzhr_jG=`|9JSUkjdcEeX7ExM@i8QrAFc!Q1$^*`K>giw}yu5F8!Z*mf2ECn!27 zp9I%2rLKsIo(4~VR4XvJOmvch_CVs#T-ii5NNWTkz=Yxg60y@hKGVT%QpiR3=h>P* z7cZ*dMnDm{I8XzTqEo?>08^gM2SJ;<8Fsi95kv0lNH3_Vj;U1rJ7L$3)VfP zTrq26x*_}hES>rRhcjM0Bjg1Ck~r)t*c#Kq!;qP8!CFaF-kU8tOJUeVqmyQ2XKBd{ z4w8GRb~Y+8b2z}A({FabFh5V>L`KoA52K(|JQ~1k6#$K{c<7-C5gHqf69hY~nDSkg?ulKz@qTp3}?)^Lgb5Hf@UZ@peyT zI+)%(_07h9m1D12 z?g(-$E;|H)OrYC}ia}LvfK~F#H~)9#<-|FXs3agDAjz0gOTb7pz=se!uQmx-4)!}6$eOleiy!_GKWXX-K^Kh?k;=DEB z^-rC2LkFa?{d_#MvCa{&dCrjzPVYc`Jh}QF@V*aQT51l+i>^wCMQKmAQ(7Vel~vwUDA z88JB|djyyF^oJXA&FL1y=s6NO;`YBkZ%ak6|3AA!n{N%ZB`cK+|j* zd=#7{F-P5Ttx8|(vwo8p)(6Md2%9ru)Y#v0AzaK*voxzQJS`}tw;0T7i*p4BKP z^btk7Gmk8hb#6O*fweNbW!17}L_Z|kRj-5;oxF8q<-hImFr$cEk_0xBsrK z25v9#D9j!N-XI!}FywqVy|wz%(xNzE5{G_>Y@k4{;g6*dpi49JMM$?~fwVRWI|%AX z^{w;O1EBL{fH7hp;&Lt^q*F^Ppg0)kl>W!!pUVT(&%$UziM3do0t#cpjwoAnbhY+w z37GB*3rPV0{y6&x3^m(86V~y1i)vd?@u7iL!qfpfbac=gtcEL(zz=5TRqGN+Lqg+P zkfm7cj^dsrMyKfOPC;((SFYYa{vz%|y`6o74I3 zt5FyP1;X&DBtnMRfXxcEcv9sMfE1bwja_#(Cnx8^J1m^`wy7CIxzoB_~ce-&8{%zJu2tt03OUJVc*F-P~n4vLF?I(%_~`$lM3t%ia> z@~8@N=$Qmx?hnUe2*W@WjZhW_F{Dx3_dA|i{)7L@R0jGMse_+wtK#RA0tv;Zx^UY~ zJrjshu*6;^>`t={tSwjtwwPv%z<<2bHGT74BMm)>(`sYO-7c({hc^z zPJ`O>-CiRS8yb;uBTJ)oHiJ=pt{I89EB?=;?qWt*fx) zyCM4A8I*ql4|Cd`u$3s6kJuqA<5zjLsvw8jrOS#vh5;j94G_UV2YC;qlk(;;o@GCj zF5;l0-GH#K_m7?9iVKQ(qz={^*zbC=(l}%BfP6a6Mog-?UwV>+&b7g~8^ls?NWiOc zzqG^<0RuHDvnySu-~1i0%Spd=-t3K+=FSRb{f{^E=6W$cU{=)#va`t$Bw;{}GKfgj z`4kAy_ynmjopPi_6Y+@i9-EtRNPHI1V76KMNZ&HI zu+TCzoDO1kJ*ImBi(reX+#wc7`d%9w8|owi0yv1Kgytyy*40s&Rges!k0-_$HEo5P zqi~Erj$NA%nupguRy+|LQaboy)ni;b+pnKNP11iEp;J)YA}D@Gx8UTmhzU?T}b1i{sJe4Bbw+6UXIMNSKNKGM*Jd z6ECs9D<3|U#ZCIk{+vyD#B`ifeMNSZ6a=Nz{zOHLl!BovwaxK7Zdr45d3ZLnWm&WH z6hj}pw3{gorxe5!l)%L}h06gJQzkeS=oAO}w>ib~jY;sz=OI~QMqC57 zq`0oJ3CrR76H6qcLt~Wma|nSJWq@c~a*jfR1v~7kS{Kw#a7MX)YsXDYb_l#QoQ(nj z>f}dJ|YtqvRra|ZU2);i6kE~*OGE!t@WXO&5;5SeYP99mf`&L!1JMexsopR9F znEbNKjKW=X4zH>iiaOY_+`m1BM3|hN_-akCPm_6{WV6)e4IID%A#1Rcw6@>*t5cXC zEBPEzxnTR<_5I8QYwOp`GtO1qUi|*n0H-W#B@+@qNJc_o&A0~iKc3aY357iYh2m*s$XW=I%)r{75ir8;S&>C55Y2Z2OSJF}A&o z(ue!EdJQ`K;P1Z4W)F<-h$GG=1ZvOqM&vra^aJSHY!9gci%@oOSdA{A4Dl!5L1#dg zTvCaX$O7r15{J}pXhgzzT1c8Lk{_UcXk4I$VwW<4isI)38OI3hONc$C>G12plcc5L zh^4KFjB~mypktD>E^Wx9uwsf=fw3Bnst@7=G5FJ%F90tkbu^?Kz_rVo9fkLb@dG<8 z-(XqnlTL-7VirsR3B~eoT``~}pxNR`V1%Q_zjy)ryLa!%uQG+1*&LtBAj|b|LdZ)# zm_8`?2-Ev68|p&<11z@*k1S~2A%2oKV!gaR5uAZ-zMv-cl;0HVFUxcLpGy7a>WO}l zIJhBAKDcIYb!%qQk)>t=3u>aPfF^b)d5p{;)Uzt#ma<1xTWc$Iv&ei}NAe3aRIlPO zHE*Y`aT=+|p;Q)pv|V4H2W9igFRE)1HjbE8W}s&k1}e93`M`s>_7bmDTjbL?8eWC* z6*&(BejDP^QHKdyE%9wM)`FP89G}_9x`4t0g48D1+DW>C{%NdSWuL8zSRl<3Va;Au z%KRfIlqEzm_0^Lch%+-=tW{KY;|7Eq;H+O*Dx@SgLsKePsJcbX{#n|%=xSh~7hVmX zTOJ%UietmQTSEh5wV~*#<%M$8YXHe?h!__I4!plg{^YPh2b;aKnXRs z>NuIu8!delrdgG`)t<(#d5zYOA|%!1L$SzNh`h*aSGoN+LS~WjaU8udOXl(Y`}Rf4 zLSz|GlbMl0d1FB`jv|n3ktm03N%+jLkJW5__lA4Pp~(s9_a#*&zME&7z-K4vmum!JGnn z5!Ych6R7qP^2yky-WO!Ezi>NlIbKtY{*fvzxwlVIr!$nc-cR}$Ym<60K8Gb%AJ^P@ zkCFDhmD~HvneiI(a2VO9NdqvVv14#|5Z1dK395l;nXF%700!V34j4VJ+zLa=mT`ME z9{)KA$eZX;Mk#X$10glOa;3CIL`9h+^cA8r)r2F_&(U-Q+Q!$ywLz!}Iy^C*rwF3O zJgCQdPer0TZCaw3lzKg?ycFi0?OVtrDXE_optmQFb1vF^N#IMY(6O^-m!My&R1*BM z>`1d4uGJo(M0SgP%>iQ?*suMlhRB6kPB=W^n|Ha z05q%b>bVf6_B#Y50Jy zGVd|Zzu$lufV-&2qAri%C?b&DFHmQSruloQHJJzO18^&)PPX;9BPcfheeq9Tn=TQK&$VJPQ|=$!mK#*>I7Md6 z(7$&T*hS%m%j@T$B;ds11&90#lpB=!g*S{goe#(&MMgj!zEf9siPN^g>1=F)yVsr^ zb_GVo7NG?*{;JHb9g@c^)?$l90Vtx>W&rY7%JZqtSt#eaGb{}xyHQ;!)aK`aW!d!= zBV3GB#$w0jT}Hizi4~4}f1{D1wO4wE8If2QpgtEJme~$Pap36p=$z8N-#H}NBQ69A zbq7>$M)DcoHX)U~fKoVc1#t_X2*G7vi<`=mwTTK_2o+&7)`^E5Day@V1sOQJ&W6XDl-<<9n5(Aom zI%5rH&A?noMj|+a##4ELRsYFI!+0&DY+FmFkXkPB9SCfZx~$rd)j3{E=!jzp6yk;G zKD+@zC7?hyJZ`Tx-7=?p+sS-`<6f;~8(d+487tDCg1`Y4fe7QJkJ7QJ@W(c8+jgnu zM}nJ9>= z%Gki7A2u5hr6YN;b{?oT3ZejGkuY!=^T9BG4*&^z7%n3hc)8GZ1a3DegChY46oWyq z_UqSil?nhatWn)YXp6$F6ma$Gz0$`}9~<4df6)BFJZL+W;W)c|_39ZYy1fp~9R{+z z8lfDvNnI#y8*LmQ9wpWkP{edVFqoc3IlCDBM57RpQ4m(DAS}0MdTEWJXX#TZ#lWQ0 z+G$n?eV^Zb`^j$jJI{XZvjQ@|*DqXgEmV3-(hcZVf%@5Z9NG3)3qS~^Y&%z` zz;Ni(ckR63SjKe&l|?vj&VX;Hx|(JM3(xry<=YC4{j*b(P?+7=ZD(O|ufnix?!0-+ z-e=-`*84aL;({hFQ^-M)`zd71R?myDAhWw{L%%$ zak5varm{~ulN0ng?8z7M}Em4=oylt@&FhEbF#%1UH!A$yfok!Wb5l#!ji$qq@S zD0}ZwAt7Xk;yEtV_w#?A|8u{7L!a_~-}iN2*Ex>!IFEBQG8U!VfFqX{lOet!Akj5N zfl~jB+i(p;^@eSN(NYs7bQp!uL-mE|`2w8l?cH=xFc$F<%A5lD3~FoZktl5$jL;?P zk9#d@?Epk^C}}_N?~r})tc8FlFgs_orC#vZu?jM)5l#8nXDj8X#*C`@Z)k8PA3grzJbPX^WDwHSPNK zi-a+4o0FbWW;^6n3uVkWp}4ERe;tJP=la-)*t?KuAehC!_U|IVg72S?^kf;g$X(9@Jwo~+ckZE`bgw3VRkYo4-i!I;%4IbGNm-bxpsMtxN2yD zuN0(oLzWQaHpUMVn4oJ+Vm$H+L0lxj!$H%!eXiU2H=wR|8*KLTlf+&FgNAJ141tX?&UjH+tg9fIR{b3L4!&j1yEo||FkQwX71mZnU ztH?)@jK5W^t`E8Nx#*p{!0I6l@pRdh4}0D1EQ+K}Wlx_-X01vsTxB<>)JVqCNo(xPEtN0U5CZ8uQHnO}rqmu%rJ3V5q}$RMuA3LLP_?v|>5y(CjZC z%GI^H5M&b7suDji1&>|%`Mi!FG?qIAod9UEQt-%ERieTP7XrO>uFSf3??N-y$JOFM z0naLqXXD~h`d;6HKzgG|#t#&p-%Z{V&tbW+L)h*MXNVq!;@9X%<*4~(CSh*GArAAs zd!G68vEgCjn~AOvd_2)82FOZ8r8>~r{EnoTWDns!LHc7@i&BDF21NiSoI|O@?W`X`w=%`p^iI z*GldL_g-(A{qKklCTdXmn>cw7kY6rIV^<~ z^0yqiK)s!T;UL7_NW&jC$f0@wheU^*)Bpk2U8s8?%8V-ADK8I2E+nuPa|39+yc9cT z@d&X`M!Z-(#9!zYt^rMDuYwTtNV8~LwU-0DUs>z6Ly(>T$VeeZM@?aI$*^~#s)TqI zcd*wh0tEpCk03;@B9JzqV3&G)UDn#Nbq_xnwY*H0+ZxA>@taw^B*Zi_GD@$h#^xsYSnof?T*!adfV>+g%AZY~O7ugZbfvTZvW^ADNw4EZU1Xl_=waEq_E z2Ae0V4=IZ)j8om-&9yX~+oaA%?>TwNSK;x<{?kq8E2EQt9o78xiIR0{J*l`RW-wAS zxTKL$Du`4#Gc7Q`k<1+R<1rpC!dObY7_jWoO!zTA3tcmW2joe%ZgcM#b@X4k)dhNr+T-gGEA7 z^ogqV_zzVuJ3>*~=WWZ|jki7on2Us^axp&!M?gwCXx4B5d;e|I)L7tIvX9}AKLvjF zfrclsx7b=_771`4%v!8@!p6x-LtGOX?M+4j6E}yNajdx#1noW`80|dkF@T;y{Hb>R zRx)l3uXs!&zx^S8olN{71FI1tIZpi~ehrP9>!CrP5Bf+~5Oi&{YahncizbCVb4~cj zsWj8hsSskEFL9v8dfk8D?2UO6XWC0DR#&QV=imf7WXr_?>srlEMxpSLHY>95-%EiD>bIb2MclQc<<1k#K|6`4ja=J%px!hYkR{&<-=;Loo0g%`+w#7IP{x z46;};b%X8LD9i4Pj*s>sqlw8VWLclVQS#0SW|prpDV`Us_Q12r zIzuGjO=abJvIsa{C>s2PF#Fz`l5hR}ZZI!#%!k$+gCDzMfQ1XRT#E3%Dwn$*KL6!- zv(8UL7GJd$I(iov&Fz~mIbM^F#dHF>K9agvnI2J%x^CLN+l;$0@K?dbiErmHl7WASQNFN^v^+?LXhDiX+oY{k#gb-340f2U~&dZ=+<~1lS+N*p{ z)!9LU!vz)Ub@y3*+9;Sk`ClSve7OH*2j){i(Fu*Oe)pwfo5bjPJ51&1cI^sNOXB6@ z^H!Hf28*LLoo>l~O~l_gI+j?N^wiQ&0v6mZc0tZ8;#)J3c;}i5}dI_psh$?Erc=`h}{g1I+z} z4tXMTp`xxH*S}ry+y!<&;GQT#dkXAfViFFCIp-HpRrm6e9w=I;LVDhUA^=ko6pf&NnNWw>blcHbcmoY-51z7#p*xZQint-qsUOk15%^p}wS(NyhWjSR zEs0hHB7#al{a!!BMEx%BpFHa6X*joi%mg&UG;p3sryY^6O`mBgg6R_a{c{Q6t9PHwL~T_ht}Y9edkrSP z^PcKPwZl_t|B1}Qna3<&kzvcmf-ws0HdLYZ&=%LQC`I0Q03ZWRIK~)=qcKER!%>#) z?Fi2a zw!mT>UsAxu)LT^MgVhanbr&NbLu+~{f?4$0lk%J&uY5bpQ!t-CnlCd2A=Pc7w78fQ zs=Ml7$Kwke@``}!43!-n{FqQ&TI%BFR%)%?R~@w$I`=Hrfy}gZ+WIK2L#CJ&&Sqom zqeqYGzlR^hLC;EJpb$?S^a0FBB6vWsJ!C~>(8$M+SFVSWVNz|^Mx?<;IuFs}a_MjW z0=DdA(7dmq!UVr7WY(b}Ku~*>mx!}Qbst6nr5!5l%5z#;Gezm(z>w$(NeC&skdaSN z7vi<=N2VqM!<}AFLqlVl#z-NRR?Ihi14nidJaqI7?_yyIZ(6=bf@K5bK0`owBWeTW z%fElG`5heEjwB>2FOmeD6G)0b5WE_;*V5VnOdOp-OHS-gVms6r&qt^y=*mt1@rNCfrg-m(wM__&h%N>D z+0olrW+JHOovTRFgs|D*jg@&$PGn}-o2Q>_!8E%GU@$i~x1`4yb3!5|F`TYm3}FML z+XuT}2LbE>D*^EINp`PyH8rEgw6Q4&xFbe))IuYukf_@cxf!%CEO60*F`+YjG0X?t zBId-!D!EzjABNT@YPBn$EJ4v#_S13Eq_|gK_lic%G6PnZiA+s58MXZHIEElK2cmn8Qq%T&f&h&Pv7evB;%;?Um!rVlyqIemtgS~s!C3aHg%nD$&+Pu z9J7&-wobkpT-`_<+}zc>b*67OUSy0X>UJ%DXm6)s*=a8b{>+I1>4$J5hz|>KaAFfa z;b`JCDl$Cc5miTS_9kwK`JU*zZFz>*(lgndSM0b#gmABosnVlOXDyobV|I-&F z{-I$`jh$T|Lvb=AkGD~sW!VT6k4!tUfDrUzQVvIbjVLdX(L$sL9z;Yv=-Z%_^d58^ zp2yE>LiF`kxQ?E>8R*2Fv4L*3&zPIr=5Fd3_1lzJ-?2hWr@Sf-ia!LTP1Nd9DY+>g z{%dd>PzUJUP=l2uidR+hO!f$&O+cp|*$NTcMx{VOy_w)!fk!tZNx|45F2wwu1D43E zieAE%lPi3goWhsWEr>X;6n!vQ9hEx&)cC)(A_6CgCQ;}V#28Lnl!E2}y1ylnyf1eY zMB+ncqauQ>K1TRSWc~`1Fausp@290_5ulc12{fRhO>(5HshTRsL(k+Cg9LpLxUz`75<=F*=j+laD(ijZ4X z4uDA6a_{Z~d}P#|k#I5{QzLxTt6ydPh>B@$ZjSpgMu=)C`pSROn%^CwNe>^yGPj)l z1wSv7)#UEt89wV~Z`SEY?UnUkcMP6TRE)V8G86TJXVH2IjxV)W$a6|z=~E^p zR={ih0&ISv?t5Qfap1GRBfsB05La^#tr8_@(tBpXG^?r1|K6PRd=DJ*yD$I{QdNYHn1HI$lFj?I-jgyXJ-u33I?3f{MuKPGV9umLAX17$ z>m}TdAxbr4I;nI2N8|?EEIRf343>gg+%h9fa8afl^g8)V(VjUZ3=^RX^!E~ve z+^}Gh#U<+Xgih%z-Y4*mzy{hh8^8a99Jp()KJQV)j}hC@fF%`VNceEsY!9t}aQE(V zGFb)&f$+dyUKwJRl+<#hB@DNziNQxi5elI&X}!qF1Ns%a>*)jW_s%}4R6c{s)K=He z#ccGD@G|QEJPEuknUfh7rstOO*Z?`7hR8O`?NdVS=tm7Y zoBH#BIjtnep{0^zuUq)>oTKX-CEdupLaxk_o`iPBet-)1;lAIb+7_9jxR zwSPNwQ5z10AkxPYPr*}tL@KEvIqZ(yIq6pEXwg33$(w=guVbh}+WlUSjupk~{$5|V z@q<3w;&6N4#P_Rbj(f~uTPr=3WW_|(VPb!xH7*3TpTh)d`4?Lhz)pdz-Nl#Mh^`hC z^NR{tc!w`OdD9@w(lD8+$8k<$e3)CFMOWksuNo89h9jvYf9#nrHft3TmEnrYwwGO4)~mqe0=XlhBV8|Es8roPIh^{ zNobI?F`gOD^s}JZ^Ywb~+K+769KtC+tQTh7JL7j*vDIyl1gBxPouK!N^QVCReTVQ1CdaOlZaT#QV?=^cmgfCXLMyw1D7_GYUa!<9lj-C#4S7F>uiR_+3l8 zdMGAuh9fEBLhu{yiMG0np}kxC4i^XyQhygegEpV=TF~kq2M0fb?L#Ew&nqf=$PuI?qUVhm@kGD>%H|5YejS&RzT?ubUBp?;3Jgz0#-FzPBK@CC z!B|x3#Fl|5G~^e^GkZ|ANh|$V-GIk+zIRe|R1zBkoNA6u`@d1eWrw>wrJ`{ay&1^( zRLjLx!#KUZ)GZfS-C64!osT6sE{+YxJeG_L-uG{6x&otk1l>z|!BcU^S6Qb9Qmn(2 z{%db(^cKW71PhCbR)UShMnu^`R$Iw%*F=2ZhU=2*-`i87($a0UR%+glxl{GgNA)7j zY+;+@U1iPoLB_oDqGK$Jm>p=JP;|n~RxlN3v(v@hR55=WOZQrhF0sVcHT5wcj6AqzD1n(;@72E7Ywe=YqE6W`q*tfe7s}wCD;C>rdQg7N3$yWSSDM# z(>p$EOBYh+uahRzK99a%8QK%6cdf90I94xJA`$_|+;!lMxxm~H4%eo7qM9uX9JbUb=Pwsvefi)-9|9>QsLsQuDQIaWLc8{A zZrk9@Nt>&Nj$T|>U%qenBO;KK{(UBR#=2{jXK$-aQ*8KGVVa1kt3B!VCgOL!td5je zyj(K>ML#yKo*JF;4ieCMRcYfn$WnN?eyOI=(`Mfb0xReL1!YMCJQ9T>%I{Mgafjxr zPqRwZu4=QLrhmBd7N7Xk^K0nQrK3-4B0gxRrFuzWVs&drjWVd?y* zyeJ*t&$uG)kUxv9`J{4@+5CSMUX49OeiRM)ruWe1v-q2aKa(;Md*hNv423K{W!wB)mPP(Y1Au{2VMj&e_8PqCfRmv4!~%ZsGNCePlgxk2xl zT7&$2{#urdW<*?}vXA(Pw3#ly!vG9F1ZfgzSwm z36(IqIj#F@ zx%ljyCRY3K{f?+Jb~K9^y#3ATNyLNLYL2!Vs=EOK`}fF<$xY+b{C!EqGQ@L?0$SR5JRMv1sM8KapYA_PmwX-fUN@9B{PMX5-w=QYd%M8Ic=z-m9Rt zUZGcUPZ#&-s|SUwfdT%p?~fE3JN_QXFE?!+NZxtY*5Juk|K81kLW%)789jU7Tpk~6 zPa-3RQT=`e`lWow#AVFZLqWYxQCTwrta|!9MtpP~1HY9h+BDJ#y&kYr^`)7I)jc)P z^WXt9BHg^X&SpF1o{HD&lh|-iCE3?Jb05~WPrs^~(Cc>a@2U!Kc|LbaDd%E~Iiu1h zt*r;&yqa2{WUrqk)2DZ9rBP*6u1)+E$9wXDzMBj+V~+b;pD7e}9hv?^hAH!)PYx~) zUnC)iq0wBgf9bYjjAt+C%soeNi$`bxZP)C2bSxBI(U?_KViOQNYM$I5|efHJ-P$Og#1%vR&KqEV_GqdbV4| ziWRhXT@27#lo%I3{b4vTq2)Vj?aDanwUCtN zR84tr!-7x$O&wewg@Yf9D9;`$efh6Tc1zq47PfpfbO*iup&%Wo;U>8mR#2D=f|9gX z<{a{`{!+=m)oHUn-@@IshyGnXv2CecFSyy#B;@)#xr!{VSl_EsXrB_S@4L`r7^%l} zhw9deQYhwtQcCs_EYKOTDG4Z5R6(%D%ili^=#alVc8v3^9)<|^qT>Z}8qY&RnX50A zsW7c0>}5|-%s_i(-?G}zor<^fU$h>EK&0sfY@#9Ro`A6XnY;CA zbO$BE))@l@7q~hcKKC{cCSbS;YR#!g-I9R8`MK*gVxLA}#?1HMBjxq5TW9b_Uv!Ra zHr1aIpIjUf8TQ+!^Vhc`W2;C1%+9;jE_17QPtM#w+w)210lV3@e6;2cm$V7C_n)b1 zObA+YF0_|}oueYVRZqH5cq3@C^$eF=LQwH=#6;rA4h3~A2#j46l<<0?Udzp}9Cl$h zhDK?l5KZ1?m_=A4>cfmfNxqAi6zYDwR)W2L<~}57m}xI*p`7x6xtp_hYjV>YInB4( z1y6csoKC+RXWS|!^0>h7kC=9KnMnCF_ZxeO*MQ`L6!15+f5e_CO#T3J&uE|atQr;~ z9i?L|0EKdID*=Z=mW>91%+GzpAu>h7RoGQXScwu#nIy$^k4Wqc1?27zl7GPij) zY=D84!!A!p>q7N8Y=qD@Fvo2tyL=!F_0h)jF-~pvM4JOIekJ;&z)8d1zKcbKBTHKe>ZXmw$z|*!wW^1K3%RJ}7wrE7U#_%-_AX^6mE2Tc8cW$* zX(K;qF5Idrax!gnUxm=c&;2H~BKqbzl3vLX``ELWH!?h2G!Mgp#vrflM;o^A2{y}*<)jY2j_@c0$rZG znz>tJZo+FDtC5%4nsxN}aV=>-9yo8e8W14hSI=ONd)N0VV(K=IgvqgLEaMnb|L?=a z`+bV_?bP`xx0|A-2P+ekW-J5>*7mmbO7Nc91`gx=tves+re1dIRp2r5N73Z}Pb^{* zyq5hrtNQZqeW8!c&ggHxJ7tg?`mM*uf6dJU%1TO+7~aqm_UPSEdGc1!bE_ODS`EoZ z3ya$K#zc+Y75W`=dVs`nApa3N3(>T_h*NN4e5xlqn%}Ht5C}&T5e9v0qE1$gGN=vl z^`9I*&?y_i`wIE4r7$1ekibM48rFf_iH$yYJj zp`8m|oDw^8mr&09nz+AI?RCe7rMHe(pL<={FIl%=z|!Mzh*u8)kGYGPvS7ZgBIr&; zXxYjg&-0xYMF2e1j+6^A z4m+T^EB+x@wm2zop%J%Cpf8>~h(wbJYB@Et6$oZqRCF?@wX(~q2WkX0tUekWsvPkB zp&%7s3&A@CxTCkz7wy;9t#-}k#HGH{vJ@ZqIeE*?rw_r*a9&X*vg>$|7yHzop`Amy!m~>UI*sETz zt_+@C(ayseG?mJGe40Fqw1$FgXnJ0pWV_!O8OG##Y@)>)Po?qkTj7Tf#%M{Vhm;Q* z)mU%>nt#BH=VJ1KeNTi>8Yqm--)3fCX(fRg`uvC4Oe+k@FSg^sGC-5-VKB{uTfv(U z#28`J^gX-G%?!W(nP{&^^&}lPq1litmo>L-XiN(2 zjULWet8DyXGoI&+S#yT;?1<1T?UP0ze)LD};z6Dx&4R`#PG@*GvaqqTo~qY!NKlGb zA+^&U7&gy*o!MK)gwxM&b>U;?TRfL&HWZJm#}D~8_t&A%bPU?yJ%{rqrQPre#tkoY znqFQCi8?i2GT zPFMKR8Pn!U^#9}=76*jCEa>$(nD4C`A}g0H^DuK<{?cT3WZ0V*AjgA;CkMVxMw#uA z?b*Gtaih?uG6lGA(v%jHFyeFu2@5YI2@+J2Pr@6eIL?5VVGEIkrsJCtTBw<|ygY}# zzn2Nz>KSG+$m#p;?+KDc4^={H`E3N<*o_Fd`NsO2>+2dp;P5-R>ssDjD(7H%xnymr z%;2c1qW#Dd*RL5Xc@{z?<5+!S;oXRCS!qswT(`u2OIt+-dwbAHe-_>Zhx>yHVnlS#vikc3njW$1y8f~?ku|5e5WuY^=$ zAxeXV+UtDr;gwA5sK299r8 z=3k%CMJp*$l42gz`kF1hEB58Jq@KM8&ZGU)9ux1(11F2MXXG$OxB}^yS)e+DMW#9? zTU8*j^m_VKukVMyMYaSK%`(cU5Kh^YAD@oL3o&ed1$3_uQy&1E`i320LdgX&HL-v~P7D@4``UTB6ta~8jhdDE?kR5MCo$L&We#m3&#%6#lu zJ*d6jot{?`g&udX=FNR4&@f@r-;|zFve;RcV_#$FDfOw_w!Wo3uFApjSyo4%o)N=w zBG!kSF(S=VUwxd6?tTaF8mz>-s;fI-{mG!4M&WN3$@#Qo3 zJN{=>UflpE(-@*dQ~8WD=Zjy-{Gs*S3}4cam#(+`lq^13!XCs&MHLmJ z;&`zgS4A^h?ff}YP2QnTlpK){g@x)tScb7TOV+BaT}s(GuJ~mGXIqoZTp&0pq3I`c zuD`SFU)*-@{6gGzI-t7X$DNGY$?MIA8RpmG{A$xJk1Fg{m^y3JGyb;eawlE&xplfI zE?1yC%*@vd!>16e%3v#|>1*xH(%=#tDdEnBvo!aGe%97=Yt@i0-IjznNzm1ATh$F( zZK|cS|BN@A`zk&K?jG%I*6wY~!-7VAL@_~+J0wBJ|$FbkgxzU(mZnbLDKC-LLeF12#iwU|mETNu}s(JD4 zQYeD@(cCY2`Sk&Z4IEz6AyagqWRxtT99>FR;k{(7(OBZal~f$kg_9C&_f>lX`K6xZ z|3eqnm#tsD?CO&>WFP2?`fq!XHZ2PbY}KGt-3zWA&E1>4%_>7Vb30dgHXU!aaO=~E zZZkJvFAFpfSZSspCl?AX&UbVTcpD1BaZR-jUN;np_3Qp$JmPKmr&l?z3bWI6MA)&B zKgz4A#gbn_lbQE7;0t8xxtzUyw)CeGYO2K~dBi|oU1RtBFBK3dK&GlEe~9uTvg)!% zoSi#~Ibr=&xeP<^*l8p*eF~KRx1E>HqlA=$;g7y>n#bAw5;=63Gb=1R^MR||lG)=k z`*ZWUw$pi=9b{O_XOF=Z=4`2d#xwKW8V>o72KQS5Z}K+8L6m`FUg}OdL_wV-De6M1 z-cy4No_aLoYntblu)g)U%PL2cdwR0YPkV zo!_=#ltt{fA=fCIa51y%o2rko&27JJ_B!yaSAP{2*VE}@?9D@&2iK1O1RC=7{+@m1 z#_g|jfyS7KFsH)7YdQU~W!=47&5xFvD)eg~4zMf(QnZv~dI;Hi(HTKw{7wW+#8GgT z)q}VpUI$`O<6~+{?4B*7#sqx+!sZ?UD4@!v+76#B=Yl18zl%%-yg_ z!T6HvU9Ljn(kIB0Vm*0dENw6_72-cV{1K}wr~wW0|BItorAI(l~J6VnE zKkUMdyxN*uRMqpY$O6CUITF3>Xog|Sl@7OaiUv=^g=U5x0eZ~f35$*v2L&*b4Zx%D z1+ZTdBY*<6f(&v_+2AP)BErHQ4kg3ek80&FdE*;ByxDkUCMWHpb>= z!%G&DlQ(t?#_P_J?Q-S&GAmcd4Lj}(4Bl9;l4aiR)x4E|Tj1{-hs^me1_OSKY1I~t zI)5NddshQd__fDG7S7UW?Y!4BqftVQo_nuBzuFkFn|Jrm7pSG*#cW&1mMO#3MJ4XS9)7bl3$t}7hrUV zBlmD$@HRiAKSAw%vR}U5vtK@_l7ick>h7zF2}45N7RmFaCoyLr#ip%$#2BB3lV_tM$$NZb9q|3HzA+|wI(kvo50F!s@gt&CIg&h>LmYo{iXo!4w%5#6X7 z;`GSNy>xdEP(Oi-KN23d)2&pTVeX&m9c&t_ZST4i5qsUlsrKwZJdB{{x_aSD15H-^ zU!@COb-22^IFot4px;;-)5Xk+XoE=lkA~wORH75cUDta5mfHL$<&j$qC18d2^Iyvu z-`gHJ_TX~;L0F8?`gf<2)C7c(v16)zJ65B=h$E>6iGz0k2;;s0M*z)n0f9iv}!sh!(3 zvC~dr`%(3J%~x-$T+!`U?9a;lwtZ&&aI1Y^LhW(wyyVgPdb8Dt^43KTOb4c278@(i z8*eg24iWBn<6Q%K(Q3iXHwnJ%5Z=96BFJyDZX<>HF^c5B%8fV*tSIloDVI!BB z{-1NVr4uEip1LYWxw~G&x2)@of<=x~jGa|CeGYB1&dAhBscwg{UOHNRJ_(WtblNG; z<{iB|%ZbQZj|r~sz{b^*voo)zx;43ca0bk@pC71?RpgLLigHw}Qr2$RLQP@TP_q&X z^cQ^eFCn4-n~=y3nx(pAC{Dhn&n@qetaY|q)hTVH8aBb4-QOU0q32q>S=r-oGv z^WgIj?7ZdQcLtmJR8D997>SGKQ3<=eyMVXq`8ea=xAJ+X2TzSoeI1dL>RwJme?k7q z$2a)}_-&!m-o8K(snxASpDbFkOhusF^bF;*p~3tIQ%V*}0tchdF+{1=a8GcC2r&V1 zOB!;xnrYn7Q~XE2_Bik<&2-E5wgR3nk4P;2MkaO2p2+8nQ{87m!SjX9OvUE8d&j0X z5zbjUygQBOpP_OMna9l!#>!+g!%z)7o*s~v8i<~0v@GtIuznFI@o6Q?1X6{f9vG}y~aU*<7&6{Z1PJr;z&GZ->mL-8o-kgR+& zzDn_ER_Z_KLRZ#IkbkbO`0MN-Su*8q?7-`DYi8&hEIf`Ssudh!N>ObM@<8njANh6Y zN)4tAngq{2=--MYu*l0t?P##Cukpg)rRa}kuJlsIXB%75G^x@Ov{QBZRk0+CwwId@ z-oiUquao|TWXh7CqYkq;G;Y(&)i~EL7G{}}@lA;O-xFR}wh*&IZm`3J@~$bUeX)2E795<-1uLw@^{uDX7W0-?7z*`)|^s;{D&iFtb9p0-FP zEP~G7x9=QwKoF^UGs`;_wh>l2qb2LQ=)>34Ja*^s+Sp$6FVg?D|8E3xm3Dh?May|0 zQhtl`DyL_i;5+CWt|$9NXs2G9D;I#knqvd$# z6baE23hzW8qm-yLy>D9+MKsl*$A>Mmb@5e=n19RoX5#PD)$eIGADy8Ko^)-S711cW zGWN+2QXrOIod+0}>wN6Jl?0|5j{5g{{AA)qCc<8mqTaGH7RV0~ri^UBpi5vr})#wt;dyu54P;+3tpj7=X& z{`DgHLjTBFanF_=+w$?-w|(!F#s{{i=sy%Z!Tb?m1ZSXSLk(W$x@9JNE?53M%NTd5 zU;Ei#mIg)W;R3?&(!h|vmZ!BP^X}W{X;xxaER3g>By}V2*s4Xb%2BCaV~s zAkdU*D3e}(__K~4wq#fQrY7W422$?g!+>Yh1T4p3*}jD8=T?#;Dbmq@pn!ArLcQ;K zTO(K4#QydjZGZ%@)V16lO9QK?wq|L5itKpvBF%>FV9eBP8;Xeq1w0-);6SrO>2mz) z4nA^l6U$WPuALk!w`@w2Kpy^LzbqBVeyKJ=_0K5GfhZ7ZnQJKx@^N?B?e--pb5mPS4?enmvN^Mf=U49a%BykM&xaV6F5}-W*I{tK_P7%gif7GL1q(jt%VYY? z_?fGD?MY}pXx_ZHq#qN+s!UBX#bV1hou{mi*J+Z~tk92BtKFve*Qing4$hx(%Gf~- zqv=uWyt5mZyBQpzMO~BLbL577S`gQxIPab#2f~dxOb4&Mh90Z`6VW?sg??95T_wND zv?;CMtp7JEPd`>{ScH7tyyx0!hff{JY^y0XOpiDe1=VEg;W@lE%5VevBbV@ng|J#S zY7)mFMHHSL(;nN7C>yEL(OVJyXfx8h}GH$qh4Lds}2x z)E#P$Jb;hUi8lw^5fXmU=wWE4-Ca@|c(lH1(r!~;9p(JVu%>+56ZPgrMhBFhWCeHq zJt2zor=Mb#6W*q3keSBWzQfoh!;27zuObd;dU<`q9$q8e`QXN@Rl-X3S6xZ>NDrX&wEeQr)G(O|<>-B-M(w4zPuV4M zw9Tn5Z=-75)ZVf=;m~-=3jVCoih5Dm2;+$!Y`61tW^6FePM{2{yY*?@K z`mVW6U%_4+rxsHy#JGAHFXr$}FLp@=ZmcTM?ta&85k=TlT>ObzdOBZ+rPH)OvdbSU zHb%}M4N@0i3}#BVFho2=9dbn`J3IZ_$-M6=r=)u|E(pxw9Jr5$I}3a5NljiZijw~0 zQ;I4Zc>Lb7N9BjPF4MEUlqrb3;d)lU{m5NAZ*N}{gC*xCpbP3MjkJ5u0Kz_vK8AFj zTXQbm+eWCqX%gnpq`-CS`0W)Ur1FIl(7w{|E$UVdKRzv?gnUM^P4nqd8+>$;1&`L6 zyN$ukf8S2)e7S(aB4KFwQjw!l=f`y?VYNWUPwGuG`E!)9OsHBH((&wg;Ik`_C8 zuQR~B|2OjPu_~$kjmV%-OZ!xH^{%{0d+XdLS^s?E{3qj~!&jgp@EjE%6Pf1+Hc(U9 zG@ECt231==qk(aw+R#BCBcHn5RwA>!HezxYf{!y|aAM?ge!SpYAG2g>%9Wq-sa##0 zDJBL-C~?WHHf!?2pOk5zUZ1gSB;z~`EBKD?sh={qi#8e2e(wyzl0jRuw?SGzb8RFo zh+Mkj{Wg6rGYIQ?AV`+6M|O)Z#@PT&(K9|RX&#f$&$o0j#lL3APCkimwt}^Ff-R~? z@WDiYzQBh0`=Q+4IB#>`T%PKF>DI<*j@_FTFl6F=-4|F;*jt9Ok)G(3aOd)rO8!yDc%`vK?`oYPP3whwVmdkR;V9RloE%bR z#>`tf>SI#i3PP0jM+757t-WXMZX`Rs*0c7f0qX}|K897BMi~qo3y2^(l9;|*?>>^G z42=P*e(QaHmTn?ouZp#u6B}!3ldCY(bB@H9C()OdzQ^+f7$t-o0OZ~P@Yv2St@HAG zDbaTbEdpmf%e=d<%6d(VVZkaOxCR277H+ZTNShP1cqx>hf!0kgy*3WY8oT()`Iheo z8f9wa^L^H?w#!u+rSBT1{$sYLV=vtU)uNmjwV&&K*k%)YA+;#(H6O$Js2J-c%N=}N z?C~?MkPvs<%6*!Qd4r&mbN_ndURi`J_gnQB4pnT>*A}PyYX;>>U5LsZ$B}<^+ zT?^+!SmoZ#9;&%NL~)jJj+NN3S;m#C-05m|Jb2D%I&ZrbdoEVIzIUuuh$HS;G%c&Y z?W+r^XE$2Psn+gPoLZ^ah14{UpgnKj|H{*_=y@d-adp3%f9mx=_5`MyLKQ(*=70!z zU4Ll~!|@?Ljq9jk=ugs2dZm4)i%Dx}B4Q`s*E;>W6?A>d*x_dF3n)|dhTPqeqF?tx zx%tpC9N;x~)h`Y3e8{SLJ(-h}ffE*IXC=0fsMB>PnhWe+cr&+Ikpc6JWK5GkJHHr> zsxP*k<>)FH7!2&Mi@lp$Fz{&Ojq_fy*IZ(UgAXyV+VKZe&CaNFy&pQ6Ufdh?RoC>C z$ld&q!);eYAesAOmyX-2Ww)}C@{XZkt{%jD!6>oKzFAjloik_bwNvadEfcv;12*s3 z+Ma5S+@LVsIW`()IQC0h3-_MCRwy2I`lds1k^4|KMY#1E?T(z~^gJ(pwEPd(-cb;~ zLQTc7ky>T;vdEKHE6h7D4W&Ng{$g{b^P4Fg{@hi2dOimGsqJk!!8WnuAqQpO&zIq} zsq$U{pAUF1igrIb@KS^@Ub^@pj%5$!XkK*nZj-Bx$2<#lNS9{l*FB))r~$W1-2g9J zG!_Px9W{0mLL4jpa?z;BlP$YHquES+lRfmcYxI3IBcE*OYmNy#8wH)%1dz@f9{*)d zLIRRs8o?^A+de_Q7OYoFTD7};>IO#rS5XwIIWt>A5x*B(<3Vj3^?iCEeVe>fvD8<< zHr8A}1KF%BL2(~@1_dSF-C_UHTNQvWx;b(R9x)~fGo3Fkk@XdH$RJEL^&+z;HmHF7 zgM9%Q?OJfRv#f2q0Pt)peQ_}?fk69C4>7Yjpq~bizoF??Q-FQ+2X$|As<;36HZn=L9-5A0wFp#G^bJsFG;`=! z;JM;)8*a@zx6ck!nTOSl)H4uRF;|0m0pF!xO!O!;Z7NUML!86hck@c1S|~UN>^>V{ z)LgR7;^m3%0Bd*i5qu7FAfRuUCr(m(F2S>_qt&1mm;-8kwWNHG%pa_mQ*X~#WMs)l zo~cwd4Adv(s)b8MY+*wP{;@9E)KoV(91~yldijf>+&$P~fWO%Q6C1RN^P0Pt&{Jr9vnl4xY!I*p0fX>?ure4nz^rZq@JJ!DvOitRWweaH)G5}^HiFO<4@8YShLOwvA|uY%tOlA@o~^^?7za^S+pb((gK z`WcXDwT>jZ-srSw5Xj8_Ru&is+{_qEt&B3qAIXKD-rfd%XK-4LNVoo2Pjz9PFU)ee z;6wnZ+i{pfj4m|AjIvpNY)=K;F%MKMnx$;<0o4eVJ8g#hNL_61a=w^vd2#Ni_NQCA zWhd(9g0cZ_d!h4Wo_Q@uG3thpT0aLQwEFf6T37Q5%vJM&R$6RaVzwkX6Sm{1q7dGz zx*@!_C~anQg0j}tQ|MhucN{0|ndbHxB@hMeK`mF*&@h{3Xdecz0c8y$;R*_c5Q}Bg zXBz=thjBArvxbWC#)W&MP+qt%?4jjd-{9k7bhDG%B>mY*qRu6=Fe9vwLf=bN`l_K8mCVWMFt`P)wNL`=izEcClpyTjBcg>FfyTkZPRJYzf@0s&$ z&Ob-qMjfZQ%49QGG;6)#Sk-<07|EYUT=gmOei+Y$y}c!65*(Q9f$TmTJVhMhkc_hN z$!m~}^U%6Jss9H}a-QvjnYJ^T7)qUv`R}&WpaCgr`a68|g$ zdB9z!9;YMQ*W`i;o&C+v^rZb-IAI>YBbP!k%R{$w0HkkTqY7bgf$%?EK2j*+vU73O zA%eslMqL*4U*+?lDBFCP^>02#d84&p#Drq&89u1xRBMFwzvZB{Dc4_7FtGfGEXe=h zOcCZdubm-ePROldRQw>H(Y5Ip$zXDU|BT}k0fWHP9c@0t5|*RP+1%#|mYS=ed5?i=_{&wjHny*8v;jU1oC>{s4hjp!~4yjMSW zT?Odwj3E$>B{cl7f3M~<&yl|_Bgwis2P9#E_NWxew!2F`_JgN_60Gd$(|3CIehK+R zop4bozV)t&#kTEap{;US?ZJ<NwL35h$*BC_LLeTH>EB{%AI)r8cGSE} zk>D>`6lXU`z(zw@I2H*-WZ1LXAM7#B?dgN}Y6cT^&5ypx>%jdf;oS=Ue6z$4?umey zwe1*1Guq}b7z5FP|31O$_jQ)(a}Q+r-v6_#k2f(ZtzU^*iGpb}#oMAkbm3mlUV}7W zCR;~ItCy@2+H}imW(RZ=+E2v*S$h);ZoMiYFGHJ9T5JVB%nCf9IJaS$XJN-dLXlm# z3>WyNpZAQFUjl)k$D2KDVu{^|eJFz-T`{X2Z7zjRa6@wR?t z9f=B~Wp6A%Dk9_q9>*yg>}X$iTDALb5w8a&Ij@g|F9UKbmnKt+bQmx_1^z${N2BlWW}}G#vs8o8~XsTovE-2UvO8wtUWdXeU`Bd)d_THLz`aJnnoEw|g<)U5@39^t%y@s{)s9D9u z`X>TsJ?vjIf2if}*Yi5wGh8S+YNe%5oewkho=mzmg{>h4WX~s0bOp+`S_=dc#uao* zY+>}o%KP;Hex>u4zrHzjnaxG=>XcTR-?M;;B-}F4OBiDy{t(%rQhh#}$H=Y5BtD$m z>2m|)<3xv81t)C<9Vbw)owsy?HUkfbFFMD12bIj?1#BT6TOSv0R&1Y}W!X`u97w`%{Z=ZMEh-8!=4_e;-l zIe-7~-98d)eEK^EU-D=@ay^^o$c1ws&#`ac9Qha?l=LqcdBf>JE>8{OwCAw5+(;Zd ze+hqIh(H3ouV1D!?OJ2)mSMX5LywF96W7+Y&rmE4B`_zDv}*i+3xTp(EAgJ(2)>Sp zscy%SJdy8rz6{w9U%qd3XPh2u|4b47254m;XpIUGBz9|Qgp7jbwV@A$?FSJ@&0Uul zQhB5>#}*DyfcJ7O&ie5BBDdD|AN!)lg?w~>TxZ%&SZO%kHIRF>g+umR`?x{WWSAr7 zgArSnqMMdrtW$v2l*p5o@%=Zj@^5B|k`jzFbU|f6HBIf8ro*wA?;2XZriOG4kw{bH z@1s*hcg%xr&ebVjxr57ca~h29w3bIk+b)T!TrGSALvrdJ26LmpMFWbs-$tD$U`LGI z;huQw2>V+R0<9v}<;K~+wm;IE)=T*+cv=si``&ZGs{kd&5=(gw2awab8NuRX$n)}*vAS?w z;axr0Td97Th3#OO&OfkmZXuja77&#q=J~STidTi&sFvzNGrN+oDM`83#^06q53I;wS#V^@+;txJ79mhbo(dhsOf2IKR+u zoc_M$jDO^3C$$t2u^A8c%wiQPrruvms>Yo5*SfA{mlosS)vA^=+Lo(TD-}`mH;0@1 zAkhtfX`UYsMu(xQ=E8cyyrG}lZ_zzIuBg$L!KXIGr-mZK0zBLte)rlIvh^R|_^;Np za4TE?J!_y^wr7y8-L|T)rSO0`{M)Ovot{REo!;Z;ZgqNm$C~JN%@0#H!R`MF74x4E zb^q^c;b)Mua7RA{BcAT$H+QLyvChE_T?d-g1$V)5c{<1>D-C?kgX8+&CCu{o>v!D} z@Z(HNXzr^1R0Ui{m?x*__mw|?rdarQMb{{)Y^bS_U{!mOOu7dCdkOtbpr9k`wnbf~ed={SBj5gX?C&aD884IHu7{SYbOLJl-*uaH;lyzBy-dX` zc*v)`HJ9-o-DaB!qc4tHwURc^uzZo6(G^{fpf&bHX@Su?{|W0X7VyNfhRGNf9 z@8;A9n&^W-C)0kHYw^Z2s0v9UB8<|y93VH7hu*dS{Vsm`rR_1ckpOoyj1ChF`i81W zG{4$Xqb{gX-(k;vw#9xG^`f0sCp^QL?VXwKQHN673x9S#{rEiPgskX#g$ElyEEbIr zF_L^*A>mBD;*_xDZuuw1)6@CZ$NH`no_tsGN2ZA*U($seAwTkBizZWW)?3 zzLSo&x$A%~8)^S|p^$Oa()RRW&y}SY|1i=uJ*Vbyiz(`l;^}hic)*##5utA~Q9j%!DN7fE0 zW0w*pZ?Qf4_r0Izf>9Ndqm~+G9$g_qW1U@xrB!c+{y$uO1yq&U_x5EN!2lUYrDIGw zB&AhE1Qd{NP`dTfjWVJFf+F3hbhk7H-Q6K_>F&P&{a(QNecxSc)>ypvyyxsbdp{5N zTP95ZdCJ^@1Rik-wI}RlZ4opw=1u;V(=7k<-V8X|WXad!Wi0q1pL~_Cne*`}?@l=& zIi=?|jIn@kwRonoGQC(}RL?(eqM^w6yYPS>?GDo`(9w6@XwKq!I7ziF27zpo~8#{G2Z6+sI{t*HaP<{6p7v!^d)Xa;FI z+!gcdPtBNOkjB)JIpwUYsIPVi3i$lKM?-xN$61uc+UAJP3&q%o^q|2_8~Zp4j|!%s zfLxM{7Cw?BcKBu!EycP6qe9%$bu|BH=|s~1eR&l6t87Sxt6Us0!G}zj&z7~p{Q=|c z1A<)BTirtYXsfnI$9t{Yi}U-VG-?IOf$7ysWZ^$|UKPU&Z&j49yf2P)S9ML15~o)a z$UW%%@YY{F)Y_2ce}s&P`S45iN-WgPAfx0_c`+AaUTi#~Jw^H$0r;6YO8j)-vMS%=IvmawY+DeHiQ{GqI{K*NPqV@3vUklpe_yQF&#g(5O z{hXM{jXXBa^xvj9IQVeKRh1a~Y&)JL<_+CUWfm@bc@G7cb=5)E1B?$HH%q)Wo^{3- z+8g>{RYP~97ykFOCB=4$FT-L8-VX7mgM#s?GZ#%VD)j4e3_N{{UJyxV{533`=l|KT zqBkPFB8MYJcqH$5q7tPlFSa{>3HQ@leCd)!qlt|aBsLeRP+KA0nsH3_k^PZP-E3qu z7@Fzyt7L<{VQ>A^LnCOF6OLm8k3wn0ihGDGXC%m9&(N3)ESOzTVR{~?TXiEI`jr$! zvL0uPa_xMJ(VlL~(6MSttQd&q(|BlhY)U=cah)3;BDuR0Rly3;E^+iNrGpyA1f%m5 zLvr=RI%f4o_URz)g38HPMT_IPW?dgY&M2?>{5xjdGQuh96juqHp3T2|6SociCTB`% zsM{nltF4Qn4w6rv;$9q#8JTGkG0MY?l=lTsYAa>@euD~Jm=6os7Qm!sJ;+WEq#$9) z`zmMMUL%1?!5Tkso#!ylzqY+zrlq!c<_y<*y|`MZ(*KGN6Ha{A!cydO8m@yei`Z7R zA2wC^=Xo4CO^x?)F90aLDY~KVt(x1~5<|YNjq=KRDk(I2D|T0rMR$B3dTEFXZtnh-y1F~vL{U+{#Gm8zbA6cF!P41oN8KzsbVT95WQk#L z$x#qIrN`~ZArDRE*|toqtMRp}2vEWO0?bO743nll%d#g8*{<`!62-aGA*2&uqy=+g zhb~AS%k(|Aqa~m}N%uLqWQzxm2(<=958t_hTC9XWL&Vwz1At@_(+W{Tb!$3o&HXVJ zxc`4-tNJZn_h=6vGe}sN#At)r@|rTX{RJ18K{C$Tzn`Qi`bc13wJ>8-VASh?|A?UiXXf&>$J zCP!(^eEiz23`K^c5I0azQ#et)TLR#DQNM$gooJkn?~i$&_hHSuR+qmS)=uzUQtKhQ zZITMzd}%#7olW2&wrDqb!Npiz<2r3FN5!6)iqpk`;{zeTCl%yf5)Cgl^UnH)c9H0lOU zVci~SM9t+$W?ue*ZJ<2oM}R>#n|%%p_`W9Zf_*cDrlIy+uah{ zG_~?8`*LMb#Nl>AWJmO?81Preca|uYZ25TkXYpLH!u_%QU@2u0k|>6 ztn#7MZkf|0gZ*MK9yvJ@q=38Kp7+!em!No>>YHAlc)iCEOJlk+ti=7_c9*7jBiG=S zOB-KVQ_c*zKT|>Pi&b5m@p#TvP!vdk?F+S{QH*)SBc4d1%3eMkp^&K^cq&FGG~G`t zbn(FM*+KglE%Wh~6VRPUz!aub{6$7SfB{uC_SzrTiD>Y%WZ=f<$O7g4uwOA0{c2+4 z0!=&zCPjb6tBYTEx8KD)BAmN3O!27c5_N0&(9qe>Qg;UwWv-DHU^$87OP!WElUeOl zsW~c(odOix%Na(twm+_2*t}vaXH2bBKTe#I+rrDU-5Sh8(UCNGyjQnw96e0u;F&~o zvE8#c3d_j;z$m_MW6$?f(TT;@m4yRmSi8?&$E1wA{z}R1`4K)>ru48nQ(o!6QpLT) zv80`$C|R4yeQck)u~*BycF5}4barlhxyZtPCjPM(ewuC)Ms1G>qTY7181+0%Sv^=! z&7J?(wBz@FPpKSQ(2`-Pocgb<+p8K?4<*!19w=#lF}A5EIQR9n9Y!l^d+!8|qifui z?25#cK=Uv6_!o651QvBN1)!uKb+Lxw%dy_K+T-4h{I6G$J<&dvY<8UOG>N$4`~)5E6g%5;3>w2BgbQ6bbVcl(G~AYV zGwyH=+NvvDFDoo=C8`{??)fmpKVG`g=+>6UOHL#eum93OO3lqiNm;!h@M6fenI!rx zCN-z~w0ji6mxrxft2#ir?-nwvv{Wo z6fmv2-4wXCScH$QoFj8yt@7!&{w_8oE`b+(Cpj>!=#CPv=C2BGOS8b-J^@Ur5hms1 zr(+?(`4wG{`L+D+!Ik^j*WQoFTGj6&u_qq+UPeg;v(DCIiD>uq!YPJZbgIuDFrM#8 zZ!S2WlG{<;)n_x0PPs66^eElR!`F>1-yNvh{zf8rx@ieR(t~M~?Lr<#*-xf|-pM32 z+F>U%c%;&1;!)nIsZ+1~tG>_VQ}}~$x#r--WSR7G!K2+<6Fd~S&NG>thU+bPpKBA_ zc!D$QW863dt|_Q_6i$bQI9o#?yWQleZo#36h#Y7YPC>IBA6E13#vY5=KJF^O1Fx_N za-01*aJ_@hPSyo3vPE#dt=99dTH7nK0xOLTWw2#CkBXT3^wZHIU&J473>92Iue=_1 zyjeSkBYgM^X=Y@T4WbJS+~03F_h8oWaVPo^ayNgg>BdfGG7N_A;(+ee&@5Aay8avi zx^UGGn{P8^f_@^!W#p69d8nIDSF!C8JZCK=-^+eZWmO59WFwz{X``E*&zdq`QZe6X zQlT1nV&1P;_p_>jxsu3Ec{p{cf(5U3$M|u0p(>Blp$lWKI%=WLlWP~ke>R&u?6bRL z3;9ai;`+;=$mLma{>kDh2-j>Vb^l2Rg;cOhYPv1hZTQk4Z~W-xM(1%Qq1eBS4xFC( z6@B^CN)$6t#x_Z#u47D~yA`^rZY5LZ^oXsvh)cLkJQ0sTzKAKT^s(*P-f~{LfrRVC zw)Mv^x8#qjDagH^)ShqZf0YkM*N%}0S!wqVmb5MULR7iLH7hI3k1_IDnRPB{pB*Q@ z7ikb7g&2G4YzeLEvXYeFi7&_0;j=>D)UY(-w;->#lgSGwor)-ttbfZa#9OS*7wzp5 zN_y#RZ{dM{*1S!zo?^4zuH7}2@3uG!u|2N7$!vvpWoQ%`=p?1Nq%zp2X7i_RsJi<) znr-`uSoFd06m^l2*1awWS;+50_OtqZXnTvof?~5M_yHC(R@ye>#(#KvC=Oik@D2R% zaGJG)w6_Z<4MgcN?m`0V`SD|SP*=V&TPdM8Y<*UuMr;yXv#;=zzq!|Sb@xF^le^0o zHmYW~1zaDc#~q6U%**XxgVA@mG+oX0M2hpF zC)*9=UB?#5Y86DuY5lH6k;^HGBeyNO`n$cnRb+J;UvhJ&PF9}G84Hz{5!6-GE;SC@ zn+vTjwTk3C<9oM9v@>X4#`ri)^ZwmgiC_IMB3P%_K^YudCe=?j$?0g|UJNYk%L&xI z48oZCI({zF$$CM-4yCzPsn~xa(v+G{(NP8YTWEa<@V|PrMMQ_T{etD#jWes2Tvfa` z*+gpyYpVe-EV5+ywac1Nyd$`w8 zw)0~y+ch*V4SOAb%?#emPi`ebVbgO3w=r}A)^!$IMbDH~oNNYke>g5OVIo%+Kdn4E zf9zVx4RPwmFeq)ch8U9z>b*%!*IKkV1X5XzE}Z80Qrf0o*R^Sb2^bKt-c%TZ;F+NJ zhi$|5MBt8#?)RDBrIcGSI4#kY>;3ry3Ph#7AFH7j(4rzDuVPH#;Xd(ZTX?%~NNc+; zJ<9QbM-Oa3pUJP82Ql(7xCHVzpdLPVXxv`X4Yr1TFCWo1_Bxdsy!yjF9He^5Zf)&4 zpXgvz%y3&6#y^7dzQ7wQx0k1F;Y+=o_|0eORZLe~36*<$_Z z5sId;FBx_8m91?Z5qoZS_J)wSm@UjVE3iX!b!y5ezpL(XNjK()zkmJ8C+Xk0g2y#n z+i7L&@>i=o3?GTz#qgi2&Yc*+m}H1#Dg|$B1`TW4yQ{dl*{isTmI#)~V0C*b!6|pj z`qrN->4{edpk&;~a0pOAvvA(|_{46>?}Vx%)|#a>j16LZxW#(@dtHS>v#V zNgm$!7C6|6g+wP%TCJklx@e&r6^T>#Z_(|HlJf8`w(g{ryD0rGw2ab0>pSR*94?lg zTL6cT#B^@9ntE{*D1vW3nVS=S^u^Yb=Y@+VlNW@x@5l<2_{f|nJx^>KPn~P8<%gYO zIB#@hILQMKm!|0EYNc{8A7Sy*Fo}_#ecw2Yg|ArA_^ikC?x7EjyV6#)F+Cp$WA*XliSYMkT|+#VqL#ky3BE*3%jkf)?zjqGhh@E?y{Im?7efWh?IvxmcT353SrSN zak;HTNPu@`#F7n;RIasMyUqtT55mFHJwq#P$JBrxQpJ^>Z&K<4OlrN4B&D6NrTZ|c zbq3k69K`&peMu8!vq>7VpI-3Zky{|;I)#$p&H9HHK1|bdYywqFh2J>iBc1Pyf$K~_ z5KD0%T!|ebsTPHyFdG-Mwi|~6YFRWzj^aqO-Q3F`+%g={Y4FcvOB(Mw;m#^qzU5>E zQ3s#96DIRK-U!FJB^o4j-yV)zR|7}*`zXo39&Uc%@a}eSqg!@mwy>l~uC)c*mZlh- zr3ZFdS>qZM2J6nRIFpk;+zlg*c8w*4SrXBncVc!Kth|4svhG57 zv{F+fT(nykJ-ByYWx*|no+irg8`d2>N~B$765^Yrc6N`RM|7BR5Tw`9=TB_Z6rX9z z>iU8{;3$KT230B8(HHeUT3_G64kp$_u1{|@d~a%%SnZkb?@NIFL-Trjwx-Z#Vsx>9 zjOGeSR9ED~ZYDCAK%BZ_tux&OA-@5XPKx%k7i=~!L?t;i__JYNwD9OJrki>nS4}H9 z5^*)=*skKFfFKf6$=Q!j=(PL?bJ-@T1fBM%a^q9ye`S=cKi$OlOj;+OP`c*TRglp`j+H*eR8vdEh z!XxHP-1sPtB`2p35wr{S+dcM%)%#l0G>_?L1T7V#VrYj#FV-06A8B(|<*rVm*KAt>f0*1a+2}Mg8}^&i!h&p}r*_)}rm7PuMAupM;O@6erHtUy92W zRGWWy!qN72wtsdiyG~Hi2%Yepjxr%s{~*D!?q?^(tOyEpbSc<`A1G+aG~dh;k(gK7 zi$4w;h*-5N5DR}lDP1PvK?bY9y@|5!2&AS9LQVb|viG1E>b`f0Ep$;=Hq)eCEt041 z?|&?z=iiZ_+1||U?j?d=vYbQFTXK3Be58k;Q( zbL#rIH{RWBL(=ia;(QP5WJnDU+$Jrw9<69pXLX(v9w=7*#a_i=;8YjLVw1nkWHQ;S zX{u?|tr&H%YHnLaUP5-wQ**fwA4+|HsyndXvZmj`&e~|49{T;+`)CMsHNySeg z4RK3$P=d{=Pt?Hqm|SphLhfIuSooaC{nYSKJ3~z*_LHGLf}FFm->qh5<4QUw1Y=C@ z&o9l+ABzv+1DrFxpPb2RfSvb=Y40xo60oRc^~WhxmW^Hi7XcEtRDLqLzIT{cYPSzw zPziEvf9~FkuX1%d+TcB%!wnusZbPc++5$et7^|oz1VSzpwbTD|{ciyjN7k@;y8cX% z4|;f!G%>Moirdv_C@6I$;(P?>!q7tZ@h-b>k;2%d$E8VOW)*|Pw<$h;@8QM)4bh=^ z4d7#bL9z@eSq!@0vxfA3tL3IE2C~T9re|ls+5eAn{dJfY-xqk|U;OdPRY|A}J#uOi zAw=y|CYXg-Pv#|Py6B53Q*sIUE$Zl~)-@|=SZ~ur{imJp$Z1Dj!>TyOT%P)br{zCc z_RoisDVWlTb^_GeTY1JX&LUpfsGi6xcA3&j$y0Y12gfq>u~vmhe*E8I-;Ukl!@@r4 zG6tpwh}kEDBnOOZ^f>TSp{6tt_cbIoi!A4XYQCmTq4Eq?iu2y7y6|$X%dtmYbm#nW^3OzIoweN2#@- zg=UlvO;2dTqwl(V$6ZL$jkpxSD-ClmWaZ5~K}}#mhg){i)Jaq&hmwK}a#JPr)X@5R zhTz(nH6)Csspt={$opb-WKVUk9!6jH1)+9{Y;Hzb1v81rfCAu5=#t}ZYqkxoWkj=vN+r$XSt3p|10m`__tyL$}%1m{q_U>5MD0(e+iOWc^iYA zFOlyInsfd9Cp4T)S_yhN=4Ehq>KUfK15pj@|Hp`g?8Z)=~K#0!$L-D*I65f9A zho#V9A`u5p{uu6x(aZ0NxBG6qg=@vFmb=n=k5pE&I|k7IE3mP2hiL?huO$QKT@+!B zVnFJ`Od}Xu#8vv<+BH}Bpk~B_tOss;nV`~fFk;za@Am7(krI3Sl)~BW$G?)wX5}! z{iYNE!v@X%F@O|h1cN~jE#^nR073$MJf7gn7aVhQLis#=qHv0z_7_s$_E~guP9k#T5Z(b*{HQ4+x01|bz z&$tOqE0EfE_uNhs^kxS_FEI}wcbL&SO~t`h2?9r{blEUql5qjSv3c2gcbJePx?sSv zgqtUf`y_|v-kM*t%S5T4jm_DzOac*+0zjurI36z!qxGy;eK-B6oX`dw7+RB zTve0Ocs~9ppF9rjlg$@ZG~a7j7G29VYFX&nH#IfQ!31j<0=!fMU4ZnovF~?F_(Q(scOw z(s3fp=yXFiNY|tX{@Ge&L9n=tENWCKS7yFqPRq1K*tUZ`vYy?Z%w}6?n6UAbr$UvU zhfYriz_5#kk*~``L!OIVUje=f4e)A{_;g#hz%qDT8iS_BCKyx+15!|Zm{x!Z6gy~N z$K>Vb$Cs4}v$C>6-=w6qwKa%wM{jQ^&};e92=YQht>v*K0}ij6-di4dm{VA@g4-9} z-b2H!nxYu+)?YhRB8HAyp&v2SHFH`jHbh>r(EG~(GNqnTzB5CVpch$PUG1{d5z~C5 zhWZ2GNasUUkL6|a0)X}AO+rPVl*ZtVMYbh>gl<@ci(PE&d5MYnjE)^OG>Pvg?JLs<(gq~ z1BRlWRBZO?e|X4gP#;Kb)9R1UdhNIu;5PD2acXcxUQCDYo!_uH)#zqaNJjI^%2K$9 z@dMx5w~JU6;Dm!N|G5WI0thWMlR&e)hCz@Lr$v7r@Knb`oq;9JNPD4)HZc!STES)I z+*Q^oXX1NkQUAq$(;|5uI$s-s&yx!btlpI^vBkIs)K0y7e|8rr#`+RX{4H9}JT8)-jE%7javN$1O<^l zm_W84CjF*A{@SB^`0yb=U};(pKC3Z+zr&S!E8iVf*0Q=gBGTOTr41`?-qdc?w#r8v zMh!(Gc98tR!O|;HeHaU9fiu>y2^7f%v`dVH=+2{HFkvA3S$}FntT~*RkIA4f!M!B4 z0;G>D*l z%}IooZfyWY6>zq}?auy_FoWPa#LD@*&gmhM?2an3?SQ5k36{zcn!UaKR@r&DxyJ!) zu;~G)LS9&F)5RPjUCuv3FQ~mQTGh_MzM3iDIMcFE_TFpL(Lnp63}^+ z;_Y|mIpY1r&hlS{*;?DSM>|SIn#};o8Jg*~o|>GpVnnN(l%yDtsguv3Q(^iD%=Z0X zN%C&4ntFEqTRIi3ZmUgvlX)F74Hkt%!CzODB;m&aD)?vQ({HExTvnS9&HYO;S#gC9 zyG+LqZ{_XB84EL-nVPm;O36SK3i6f}h|H}|_UidJLGwDVadbyWw_$`u@S^LYbDwEy zI<6YSVSE_NUDkARHcE)(a!AOV=gGd_6Wyw6Lv)%Gh;QdWkrD3$-=GWlb}^dJ%AcId z74?@c13z{LcX`*&b#vi_gT{=R^!9c(ai8^SICgarvPOt_-opSL1}0m@@TP~$fx#XF zSmaK2a0GCY9CvN(>?Q!Fu`x(RTg@i$PAM#{!Iy%I({8R8G2hZt@J3Cy326dh8uil@ zgX=^EyR+c*=MN8%UZ^d>!?64u^ihyvvpGnb<{1U*N04L-m&Oy|!)P6C1BiDpOciEN zG2mOiqOq&x>CyZEl=qM}d0bn9E2Wh>t9;~+QF6F^290hzhuzKXhPkkaD}FaG!H=5u zL`HOf?UHEFJRK6@xPg1yae-R=6811<{ILa16@9Bz7UDHq5eAe}j&p+(a6_9i#X$59 zaaw?c`4r?x&L;4i`InzwMF%^xs|)rOC5R1sK0rI)JUn79Exp^iEfuF3A}s!u3~lZVocS>z&*@JQu+|-A+hNWv1I{eT4=@TaaM%eyN$bj%cun1k_4b;gBYO0!-@FyC(M1iys8SycMge!wBUpYqgnvZ7S!I>r)X^Vi+A+Q0HmzVb$81Z1j=pOJl6xrC5?D01uk$@&3_kvE)EtTPUxX*(Y z)*|N#QtpxkGmAJbIYRb#*~D= z(8~P+;NwK7h{Yu&6%-WIH8h}j0PiRO>;u-6l$6Mt|8|(Eik{n9FKBc|F2u2#Eg#3< zB-iDNG6+bCH=1n1)jaU9{*MQ!K#AY_^k94qa+eUQIN5Y8spg|HqSI@*vH zmN)Xrfn3cuuOR;j*Sjir6X!$kMfyh#2kY#Wr%sW(b0uU4ibDOxEL~N}=EBZbFs599 zyxkZh>V9j(4E#5IVF+tM?`6P+s0AeWoSm7}!zcjDMZ5&BF9m9W_%-mJ^s>V1iVLNy z&EQ#>A+^8{u!V8f#!UBpGj_5+h;Fc#4o7HJ3pCR_FA}{-V1mBE%I7+5M)e2H<%c@Cd2j!2x8-cDum9l{H8%21KDjK~}hn z-t6E?4Yl=sT9NbuBGA&2?h(*qYY;di2u8sPEv&AOKv<*c zhza5cwA)pnE7;iH!OkNA95~q>3s=;R#Oy~Y5utR}LXmzgo4^Y21>)F^3w=l~B$c!d^xtePsiWr**y*QaxpyycuU$Kc~ zhayX5i>~Yv)X(l#q?I8t*5?m0^)U3lYp{>ik(HIr0FXXJmi9RCV|(X}~sMGxGC!kZJ+q%^-GSg;9HFuoU@jEgEbgtGO}zEOgto5<9N!AQ4}2 zC<`9v%%Gw;3B9e}e#;fyeoUC*>4$3Ww(P3J27s_&3~5NJjm_G8OeV0J2##zw+vmVk z-P&}ql?r^CMX`H(lkNvbaHzXB^TfQp{{TR;6|8T^Mhms0V`QH*(q~b$6a7NU!n#>1 z`euA+8R2^X8$d#e?M-*=CWu6>-^X(=p zw~oYFH!u+Mg+4}5ScAxw6#`#_Xos;kZUdh**^B_KIE>RkN@SkV{!h^`0HvlVlCYMJ#UcP**iro;P6u`+W=GumIL#Pmfcq@Q(wU5bg2DEA;_*>WF z>nQwBiG16QoBgZ5Gz19ker>B`uN-u zNOCck^?fCt-8ESU>$V8=eTeiUo&cr@XG4U$f%#<+mdWM<0oA=jD4ctF5p)a9%?i7A z?TR|R(A{b&7|;0dQ((VWIOH0FIy>zlJLY8u!D`nbASHo6+Fk1lcSRx|Scj>M$oBZ! z#>PfnI0!kSp@=rn&*E`pK^ae4bfLB|Q$vV@s(deEyd3{SeWj+1oPxp!KnXE~V59{` zXt@BKfTdz@#l+b7KKRxE#HU+WSb)V*;WTQFM0^X0@R}U0-vp4?<(L_teq@6(RS%W2) zMe?@w?P6Fm|IXAWYX}@Cv=*IIVluJ*NQ?^B-hFP!-a*me;9%KV{{BhHlVuFzQZGC% zYk6ncumBk~>xD<}E_)Y$lvY$V$bND1avBTs)fXQtU(`#Txo>ruf%Q>1mB63!lJ_}R zclF#y_7~h#9!&^+u)gkCkDL zDIY2%dRJS>0RQ?UU>z{?^)xQsDY2W+1FDT8O(N?*JSv!?|o%pj)oju2i&S9XTYR1!1~K&YL`K^ z!Sbp<`#fGHwyc~*rKKiuJxWb??Vpu=Z7s157au)n*j=rTu+A9MRIauZc)Gj z0nFY`1f3cPJSdwE>=6J8fZMzH_upf=X3!1Q0t9DkUkT|{o1YbF?m)Vea!JgK+*rv0 zk{SfJfKgCz0Idv+R{3`G>iFq!AKJ3)AOwf>0SGsAS-2P&?jtZZpuiXZ`b(Fk#~aAN zje+l8z3x)fuWf%7}w-1Q+4aGZy?}cP0jDR+xV{!cT3k)L;eouVtRl_ zl+WJ}U@%?4*b%msgaNDC(WB#@4&@bcW96F#RwEfedP?Q^B@1K)z|Q35{;E0uhlglH zu7HZ(gm%!tRN-H=CX@e6H!^*Q3;0}Fd24;PN0z;G=>;XXv91ty(tM~`34v&VDBC_} z+@Fe12FhYflDw$cC4_MusCRhL0t0y_9SC9xe){PVm_g2fX;fxW(GhevY#TE4j@tz6 zMF4LB`J`${)h;VWgcYy_DI2&kkettqR8;U<^y{-&19nF^0A^?P~f7tU?+5h%o%E?tUi_3FEt45zG~ZyNOX`SS?t z24#yo1XlR~-Uq7b@eTZk6$OPM6<`97oXwS#_oo$J19)TdIs`+&cO2X+7Qy>Db^bbe z0|4um13&IqEi5HPJwK&8Ln{?vtSK5I0Y3!}#F7Pf^%d!jYk-F=7u&tiqEc8FhMtK+ zhupipvsNU}L{t69LT2@szi0}OtuQfU@!FD~I~NDU(9GAbSIgyo;{#y3x0F2f=w`5t zDL|RV@K0r!X9CJfMsB)q2_j9y_Oh^GtSJ&-|b5bx*!XjUg+k;Fk_Ja8Zg9LExw z}?p}T^ByJc@9%q!<*h2mi3oT?7s zh?0TFg*C)y(Gxk~3tB?waLQhZ?TLT%j&@f5+X7{U@%(`ck2|l}NYrQ&Zz6KS7}(2j zKuyvi$(3w}UC9871GQ#;STWGVS;n42Ray#b{5jh|_WdHQDdrN3|Ju->pT3*7wY4G4 zsMUTnX1g2_S6_O0&486a;G7lXr7r3ffw{Y9UqgtxkO8qz?Fxs6v2hYYRZ&4__~C&6 z^+rg=Ue!u$LCvo=$drilK@tOO76$qEAuQt!umwcC?Iw8ai9i1|20%JW5tlFTOM!Ro zo{r1%%l9mY0xW}Ehl6P!(brs`Dxl}GS$R>UaO1(5Q^D6anUfP2Ngh3o9kO`3y<(u0 zEH4h^alvR>=eZktIvWG5SFVTwI$s-@IAcZx*jT;7tt?I%g(|TS3CyHb$O#Oduj=_* z)b;8-jaiyB!n>wl_qX`nyW|brNQQx*c;0r!t|o#{|*&lZks7NX^Lx7N^-M_ z0hrS#ITcRBP1eHH4%vD~fj!G4U=&Mh6qw&A?vsAZ$iN^6FiXH?$=RUBs-D*h)`vI` zr@}@@_8j;HhYnn9qaqm-M{d*%UWg5$JF67aeU02aw+*3V16U$y3P3eMAZ9LJyl9XV zv|oO(V`d9pLs`V>^vRR05C|xytMhm^p-^`H&S|%X=#6IX>=5s{)s|dNHv@n>j`qiu z9(X&zYH*R7+WcyZ3MUuW`lu&~Db(8Ut$<~Vb@D7QW^DO}=Yg%gAKYD-ci~ZqodwO! zr?=wb;=H8zJpuZ-#*YRet1`Q7@5Z9EVE7pzp{n}x8`{GM;`bg%-++JEpRM-_Awfz~ z{n=1oFAwg|c5CTzgBrlGD1rGudhf}{hknc>=7wprQj?m+{Ezf+@*?f=XNf%(bDwzp z5m^-H@_wT#Pe$O2RY|yjVDl zo$0p{O`dPnXYAWKyBL5lr6JUW=pxN~D=w^jd@91u8{E8|-E(n${U21;4kl0@muFk) z;>V|hFMH4ICtL-PZ)sKyb*in7ha~r#CLfVtJrV@uC)+gAEFC6J>ZZ# za}1;njEpqurvah~3?xnyP$;9{FZMp@*P~C%6ar4&7WNl!s!cp9>gFr7IjsgH4G0yW zXME%@0PRmvNc2Oa(WNABUIz<23nW5a&~Ti_4*nEoZ$cxuI*7FetRD&nWrv*H+^%?O zG6+kW(8>-)>hg78J@mecfJ)LQ&x^IF85cD|RjKBF+{N`4;hCNJ(x| zhx`%giiSy}=85;++`&1r>QatyC&Fs=ZsIE3{B?)k&|@+9qRF-o@{s~hK~xGflzMIe zhw@q3!fo$%B5ar#&Gm28PBs+2(P@a4-*F@wlS2r$|K+#~&^{#EJeSH5d_sI53X zcV5oMZa`>vg<6f<_APf+)zAZA2$_b;L3U)2<8-Kv>C5NvB{2>B*p}T(XCPo64S{9< zfGjip#JG{9N;!`L0m@@+W{kAqyZQU~?-6R2xI4{DP@1CPFBDL)>AAjAPimt~SKSJ~3HGfa;@{+s{qOhjWKEHvbW z`E;*l!wtB30gO$b3zU?mv4O%&ib=7uBHKT978NC>oD^1`p1y&)Ib6jWP&#reUNKo7 zam_^{XAv^psvkd8&-{!AMKE*Q`GNic8OcY$C#PiiMU97t2h<$Ff8gV@VtxQW&#>yD zlY9M~7g;eX<5&9QYhA_KZNp8}+Dx_Yn!p`4XZ`tAEml$_AM z-kU(@Hu--WFzw%7o>9s*e8?4L-U&o&Wl$dN_T&us?6YTi0#%c{h3#_PS$pK*@FkA3 znOC_uw3i5dNKl}v3dzzO@aDSuY5H)K!hku>@BjaN`CSq z`z-JRKi)s)l)`rHM`4>k*)@k*FOEBvmFm|8%nX$je!bItk&4QUb+lc%PC+*TnkwD* z@_NyC&CS!`URea+Ao3?d!n3OT6MP_fv?1mtlZwoR!BjVUFml@7_6<5M|_8g)^t`P?}aeEIT~E9+2Q zr(hZOOT2PJUBT&~og0S*k|TgZR6 z$1>eWW_TJ8)z~0lIUP6yK_LToo`b7bTehBr8D5xZIO8A49=7#YTLGmW8>xGt5ae^g z-HSsdDk>S8H;xwd{CH8HM9-d7f~d-_$3sEi5t|hOQnl!|DQK;=ck9(znzR#3PcNY^ zuroHWux2VI2du}j6Gm_Vw9B14X)iysAZn=_U9}qn;A2!wW1=i0dPaM?pP@6jsy@F# zzJD|LGGNi^C;8?AcHTp9S{qKTw{Jh^waBrfq(zSJCh@VV-os;KJV=&LQ4b!Ne$w)R z-5_eo$q`_lSX~|G{Q&_@pVb4_%pj7Hpn8Dahu)D?kUg){{4G#*OoOml<4n;PCA1C~ zu%_w;SN%+ZdQX$wIc!_jW3fZAfBH+_UW&G`r(yBSFTW?N_R?~4?{+rk%@~gkNSs)9 z6hbtJs_~h`i`F*Dq?2tz8y1+d6uM8=-?S&+$n~42$@^taK=Ee?;Qg z)p9=5y6-&$iJP5Hj42?Md&jCZY=-DTTBJg7aLtb&V zIy7@O^$HWK!}r zHhG&qxw7|HLgCWs4#39Iyg5?RxAHSsOqNn2pn4U40mgn3(vU80hIm>bZR~X-P>*0^C%H$NtfB zD$meZooUyJIr+iIv)??b#npsGD-nH@_>LJ9m!2XDg((_WmN7 zHBtoUWBb1QyZ|gUlaSD$E(NwV_BMWI59x&qQ&W%36338rczigrJRT>`JH47prmeD` zTz%3s);wRejwjZz7(qyc7dzFEx1rk&?H2K)qq-m!i%`c?M$QXwJ50Wr-})sAWvKdP zlhKdF3rixs8A&Qrg^!9=1;d{a?^GrPIShRH<8{}>&!5Rnana$1UM$aw&WGb(0AfT& zP(H)7%JL-nY$7BwV!?Da%o!x)>a@nWPo?mH?L+^%#_@qlK&+O2Lf@(4lL)F(r4&m! zGH*vU_frQ@#Y=VOPIKc

oV<9JG{2@kfPff8ag&djt{X$O|(F93TBh6d;&&#CH}F zi&<28(T}vR#K!&u^5TOzZ$0Q}2%rU#4P&P)Agtw3{Nb+Z)|D z;_B!R**~aX37HqtJ&6+6nYba{Fx!=Cl=K?=`puhc(su^)MrTn=&;MCml5k~%UjHK#NOX$m-2o@>gDb_Hoyy~CF0JWbM0AZ^ zW78c(AaSjd8aBubxhF23{G`eo6o;@u%*9cq@d6$yRPSdr7Z+D<@33{Wpndah6(XNc z^;XjM%O0f!=0E(k7u5BL5J!OxA~PdVD(<}jRt^8mcO_+e{!Jq|vGS_(ig>7rmt9$TLX3=cpl6S_g+p>S zxCl|Py{8H`)GY4ZTP`Kb-zoQ5HNwp^dDDW*BcG-Bx96peXFE?0$-fy`yiA{$6OXA{ z{&}ZvZ1-%ku49i55KLroXeve0&E)X5R-b}9jhjDhSy)F3tztkj^dP?P{^Q%HCfM!R zFDF?HOdJgj;j13X$`CC zx5JzHudUg{fBx)1r@Q=I2Ka!V>a04My!rbY5$Q5JBr3z~Y_&X~1iD_}%Al$hKK)Yy zsYS8=Q(6#q8Eg;P?QR)kLFbLMLwPmCWh}QIeLJc=i72yui)a7!jfaO<@zPhj!sq91 zpOZJfmKm}L@SH_ORkJ^r!4x;l+d0$eK70KaYLxzu@1@$|z+#j2+C4ij*Y#b#);bPsFYZyxvL z>9Ca9`;^S!qg*O4GKl|hC@Td5V<3yWp<3Iwu`rYh0Co;^e^E0AmDXuqf(*mcLsz;& zl)`vb257K|c#uOt?cO*H$8KFRl@pd5(DrGa^^|7fY}_4^{)r-$C1REVkJa9vZ4=FCD{GVPIV=#|Q5fTUd`xoDkZw5t_uoiR1hA@Rco=nQHrgo2J-pO< z|DA)BUBG@J65PjQ5De{1Vq#(?sI(9l;8x(goQx|epkA`vlyh2m{ufQe`Q&GtUJUO_ ztg(t25c@+fQ~=;?DpvQMr4>qAtiTTMqEuoP3tkr8L7^sdp57UNr~=UcbhCWj0Z{TF z4uUdAy1yo0V}%PlG)%fP~CPoe{N0d5{EIu@=23 z)sBvd$#h(ufJpU$bO>7))RWR*BCg8A{@bj&y7nMJKL9^lnYcc9;2U=xIkQnBA*RC> zHBl;)bV~gC9=N9cnRb!%V=c$xtIu+;?=VZA>n_h?KTARe4UbR>{HlK6k(TXGk0;;9 zzCVTNkQ2;2wTMfvx<^$EKdMsV#pTaQf-K_D9Hq zRc=sqkER$3=tV=8h-ZYVWL{o51h`(|$f7*B94g`u>$w~$b{JF{)+Ua39O9QX4nX z5`&OaI+uyCusnb=;ptP4|3Ym4t+ua6HqmWdDp*1?aZq5({|_2T##RiAe|wIQRKo^? zutq)eC~pOau z)pHHs#`_{@%5VdY)a&T5TM|g^E|?R}=o2tZ0d1{VI)ol>0O$M>su-C)AuMY0z*DRe zFBL>ofR2U+)M)_jU3PMXI}ilH`beMMZrdH9L{?}iGKQ$%BTO-{i5*f}!U|Jy$XQkF z7xc_R$1sm!BRPU#s>{3^C1@|hUbYs1iHy(nN_E{mi4T_H;L@G9d?Oiv(dw9Aj4;Q$ z?IhpxC4c-SgbS&cK=IAAt6mQ5f98+sYKZQ3p(?`zd9&?IXPq3gGmVIgYG&C+I3}{U zSJkTzuo9A>5*pmCQ|6?EkQXClr%CTufq=Wdog+eje-gSMP$-!Ty7oA$i9)&aLiw_R z0&G=B4V}ArnXjiWov1dXirImWKHVcmm^+ZF+TP2qS9t*8P0aI*7jTQA2&Yy*`W)hs za=6S$h1e*VQ;60dVf6)fL2hag6B$XpzBr--HHQ2^g^zbn!6ii(v| zoCp^$5Q~ewjWhxRg5d5FO*RDjT@I)IPtP6&5hxa8;LwJc+>qZ0^g+OF@5}Rh8ybLZ zj-a9Q8v#rC>64?sm{AOt0ya~+Ci`%y0uZ0(NpGzYG8slj#$Zk)teiY^=1d24#~AQW zLi>RC+qX)Pi9T1}fUa8Kjl~fwej{K`52h;zlPG`dmIsozGF}lD)`Ghz5S)gwDj%dS zB258er3}SEVjf&vKKaxU-h1d8va3M8h_s9)Kx20;S_aCU`34Q7K@1QkFgstRr%wfz zo%-6^tU!enMMlKbBX?MxM5s~r_Kpsv3{8H_G+4mOckG$SrELhRjg016Fvwqe`FjDA@k%eOkD)Kvl+oHba5m z7{IuLbKy`k=jP09r`xM;<$n7R6_pN6aT2?mi-nOFDJfF`Mx6Qbn88EieJc*uGppVB`0FI-T=lnY3ZN371UwUnGM^R$Fnu1| z8AUxuxUFo3N_JpOj1@}<8A6CGyi$9m+Uek6vtrT)T$YP2B*7_;b$)=%{GF8)=iau+pf zZ1>0%;cDPnC@kF>Q?ni-0Kd=mQsxLkKP1`yp&vor$t^ zdX7*WR|ZdG3!%AQ*mVT27+UJv!2Z28D2KTeCWQoFxR>?7B4pbbUpNTp0@u#buD*n@ zaKI0%*FP=N1cl}4Jc*JE1u}BzwEhGI0=+OQK1)StUCM(CalJ5QpjlHvF4dt3?K{yN z4h=R^L7d^RoP!>O-v~;|>di$?>nSkj6t};-Nd;HyoSdAUWzP23=;<|@;f^C5N_0mr zqIpy{LH!?lXCBsb*R}l&88SvGLm^YjP>3WVMKV}XE zNXU>#l4wv-2vPKYcDU~AdG7b0_y6~I9QVad>O1Vc*168}Tx-jVS?)gDw(;Cz?R{7c zr?~^3Tkmn>ti^pm{O)kPyzGTJ$L|i?-%_HQR_N7cT-Bh`YZ$BNCb)zWcLj?b*s_iT zT0>crB7v=rry(lf_^{O|e;n>$CZ>(^ngbTR@{NofH2s+N9Lj`8Yz*5G>rwHA1LEN{ zYQy7Dt6#8mry7`Zil#W@Kw->k`8%KENC|FL1uvhKEazKkd^*_J8m4*h?wgNmD~J4! zN=Eyx)qO}@M}FL~V861lE*6|f4xU%fK%WqDrQIf0(F z$Ec_n3iV&aC12lGZ@%l4Qq%|~O@o6=j*j!pTf2dCMdB7dP}H9^soMjnj)pt$S5wNH z^K}OTuT@vC@Wez;Ts|DAZKLV_>9ja9gvcv@2YM_UOy%}?`lzm)YsTm7A4!m2(Ons* zTK4oXBIb`eW(DO>&)7V{yt9fv*4yj}3_MAQdiN#QCKuvwN9h9&Tqh2VI@X5w2oWGw z_MSa^1}%JvcV1||ETRP`=DZ%gqR@*AJKLB`uJTNGbf7J~awYHbC1y!2O5T}(rNQUu zqg#ykAi^Zne`eH5cEDiEcq0*z7zFO&vI9C%q!nuCuTwZmq;@$*QDgpH@#@4KZ~6HH zhYXo9G`TjUY15_w^AGI_4h|DWmY;{0it~4LHeCDbXZV}*JqlsfogSU`37Ki+m@fyIpbRqJqkelv;sSL|#JPM_&B z2iWn18$<+f?Bkxi`t5CL0i2#K)&Lxu0eVDqsJz0y6Q@?eukQ(I1Pvj*;w9j7#QzUBB+;|+$gisvL)d!v7UqUw zkDz6+ce}2`Zav%i+4;)67gv_&a&K{q9x8E3-R}Gx4P}=DUjW8`C)_vn_zBGM7F}}4 zXy3;j&+K+_fD?*-w>zJMxE&!R<$#<6-nNrh8{gBjCJ-gw%N0#(3GhcHAo^#srcHra zeT1HjgTe&VU~g|PBP;vmV)TQoEK|-S*|T@=7uDiMp<3mm=OAeIrkm~ zS3JId5{F-K)gBuLOL%R3k+>$B^JwfUn;$X^Qdvyu+d1|;1hiu}?X@w&arKt1T3-G7 zrf{PK62LFgCZ!MTyKI%B=hsQaEMgzSwC4?p&Es^A*L;XxcJ^YKpUPjMOm;b}*y$nsvZ{?I1q zVPY={*S>CwGcF72ATuO=-Q;-E;U$mnE^JvSG^mZLqiYHtVoLPf|0;6M@ae&76G+mgP-g+9>$Me^y-Kzc zm5z{ezgN1}a8TGHD$llWMEFuR|NAc)^b-oJ@uK z1fCsfEpw4G$#w*1J^*$z7Jb+u0yoE@H_N*i9neX5F0?6LJhvo^# zyZZ0SM90PGY#kQrimXI5)$IEPJtVcSkdioJd)Z3lflU_BiP=F=bouhuC{L7 zy8Q9kT`$exYZq~8qUL8S>?ql~b*nh9NuVN`E?pLksLQpyx@wAuew=%?#N+HYa=`oN z7bmN$tBZAiUxTv}cl6XLD4TNpmpx3u51bhCMJ*(*+eKL?iGy?&v(kKAl1Oe;Am+qb z;c%2uP!QYtSLM$Q*4D?wnP(7#3Zq^0sN^5GcBsVlkc`2~HPK0YpE8}F&p zKlk*oB041s*jwGlg&!wgd|}3)a`^cC14C`B#Q`m}$G-b~^x;s;cyT<& z5oId~2nI%LSn$Sh(r`tRD6i+OO> zmvXZ{2|A*b6x7OtPrQN4cq7{N&@_TRxtQoblU=ujCg8R!nn@J4oN;{IdC+DBYV7LS zu~sbJrMmmtuR zrwC`RM|Cz@6Bz}w@zTsA1nb<~3-kSi9dQkXUUhd-S#rCx2-GjlmWo<)@SF>JluwOI zl_+ce7ZE;t%T0-ACCK45vd8_XC2<&uMa#DuFYQVZ7w7zP?%A2ghdik;k1(AA-8u^y zD-%F~1j=)Q(^lmmK_NnH{B^B;&vB7ZDkS7CPAM;zr&tM->$^vxZN6N^4nua$rW-ke z7G4e}dGJAN5+)N-yRdm?-PwblN7Qi6CWx{U1Z(He@P#{Dw3Qc6V!ZTf4pV_KjmqAZenb!sd9Ydn_VAZp zH~aSMx0tyAuv|x9nhhd^KI9y^Ckd_##afkUgCQyh%zxfGDer@d&l%tztv=DF(bi)d z|6anYPxEJ1;`JjyXtrtH`XNnwm*9q|U0+yb_~MSWd9|@&?9HXPT9$yl%p)yMQXdmK zJ_w5p8&{qqtm-47^aM#E4!{EBS4O$sMCz&Gl%EQCMhD7jFQs0Dc;UDu2p78~R7SmV zg-3ddZ>($MAy{4;Z=#zte%c2@60&SKX=euf>+0MDJKC`>()%7Y&hy7JwzW}K4?JAJ zfLzFg$|(EoI@87?# z&i!siN&J}m`9kq4Wc<~+O(bMy3C@i&x(15znN_Hf#_|G7alk1C9PvKAj^D}5e1s9Y z1}#;~Sy@|2Nf)N|u{cr)toTCw*xF}z_6`u_*s_;b*`DcmvZr&9mD8)_Byo4F{lLE| zX+>D)0W%g6#C~u>T_j{xVnzJ{4#{*PE(ky>RCIBt!q3P9nmT56WlW;s$ub0X0-hY=(8{M3zY-lR_A`4 zOM<3%;XIg02+`sh@X7cG^1lXIkHE&-uW#Qn_UYx-xT(d-fLq&k4T0;!teKbBl4=tt zv5oMmTDup^!l@@#)avn_1sXeGy>1HSdE?XVUHag7uHdkUN#a;3xXjP*z6hv$dP-+u z@+PE+Dt~4)&+$mrr{cTB;5|?t#cRHOS@Y8EfFSFD;d!hgK0W@brxH4-Hv-m)dVm#U zwr<_Jlc3l4c|@e$1Yt9NubVhTLZaXYP(*kot*)-R^Fd{j8SW#3W8%0)o2 zOO#`Tz;Y7PQ#>6(2M;bDIGkKd^W-^}>RGJy5fp`-M>p(=Ji1&z_@&3RU5QAW(cv<% zSeMsoim?VltWv}ilJj4$i-e9iN|1lPnD8hzJ}%@C-n_$ih`3yEPB_(X1ZDy1j@CA} z*q$>u=t!J~$=|=QO1l7ewnJWB7k;@ob)$sWGG5R=04NdfsJ6QadD5Ii?1&s3(iH0L z-IVaC%HAcOnX?Lf@zXkh~PMRvFycGIDA9UoXI&J3?+EudL#`5qmiWO~@||>eXGOr9Vb* zYkST#JcMGMI#*dsYp#glJb7%5(lC<-rsY5cN{8Go-)JrJOol{IaK8+&@{=+UFsF%>{i z4^{~-X&E+KF%Q`?F%$ zf384c!z7^|f5;DqPN+aH5=|073z}R9B)t4k5!Fsg%7lVLwsYrG5N-omU_c;K5G(bq zr9su-@5!%6w*5^B#KZ3U4j%j-y%FrsPNljOY>KFML^eQ)|K~ILZ#?|xPa1z?q};TU znDXBrWyQY{{(t_RjMTsG>0f{4j@&D_O8h>k~{Qtkb#Q*cd@$Z$` z=08tzyfJKZG`*O0vu#G%UYDRGJKwQe>TK2m`R+(X@)@c*aqwW9aO}L}AI8!_0TBCf zH97%;%8jO8Ig?Wjmuyp;9;Ft&8of9->L{DS%m6ZyJ@Ea1yrG`Aq4uGK8_q>1(teL? z!4o)8}0if*+QLJ6sM7J?FGrI0Iw z2F{v99>}HfAc~6a#X3O*DF4IKrUV{57>6A+{IMEtUtBG2@8l zNkqaa^g;+mg4E*e?QP2a=S0q#B^VoMH*v@@SgecFgZg?8QELKUh~C>)aY=tNMDO0c z8-DB=2cJp6G=j@SS8?ExgCe*ZpL)z)8km<(ylRI*teZ1XBBweVP;*^V6tk zRVs7JWV(g2CyI6q&tLBDRI|zTpQZ|yR5b6=2v~dgDL13izyBnxWukpT&~uWKAAMDM zbVjBhb736$RCkBP%%!^Y+;CLz0noeSQg)`?fwZ!=E^xEyOtXZ?*<7ludx%}QK_21v z*!a<{;dAI(7ne^P>#JzxGde!bQ>YajD|&s=l?KWOE75Ny@H^<{W%Uo>aQO09$sz>P z-9Mw}h~^TO56CS!Bu2*DDyc5bwMdOD$waQhBSw-1o0i8b;};x%Z*$ zil%fEHHgp+K6-EiGGhnKJXU}2c<1}U>JtSDpPl=?{__Jk>iw_ke^Bj2N4=>Mkg?~6 z+ShUoo-8WJJuBZG8xB(+EiSs{C+N7@6*P?o(JDwbeM!&$4~A%*KmqUCbLriC_rx2n z!7{JhuV3-jTuNgIhl%K>yT{d6Pc)0PFmBeOZMNt~Vn=m-FT}v>8~!Gh!cp&FoZ-gT zzs$8vaM1_ZjC4tYt`{N>Pw7K+`$tv}AQx+X)m4cKS`gHNp?zbs0o`Pts31lzFIYNI z2(Y4RqoX^V3W&iRvji7M>C;w2val~jLSC|`z>uLlg&EWZ!qOoMce*SDq!P_q;+$)) zF&qpc8rm?7F7;R|-yqH@hDs~B8cl_B->t!O?%cfS1=X)QRL{h)@`wiL)8gz@DkSDJ z?1PT36m+GRl09_EA#L{yvkwoLc6j8ymOK29fjg|e9ebHO;K0i;ZgT9e+BZKaSavun z>TIywHTG+8!Nvwp(7F7W@a0q@g7<)io?@ zg_$N#(7?)df2qTL6ycwc!fs< zn8?QF%NDlL2tXBr7=hY|N}O5;RPFck^V>Tl;rfb=DacYe(Kb5F@R`K^tZ}mvhm6up zHI^1?(Jm#rxkd`(hL`l5IdjBH7c>02&%Lt#n=_)&oyq5I58xv&hnajhQ|IT~z4e?% z4t`w#n=+esI@nPhR4+^oh_xqIH-r-z;i$jS0LiB=#<7?9m3pBV!gw|g zR7m`XfDuS8_nXt3NTj4u*w?_DMVvTsy^)tuRuYSgXZ$j|+l+;(>kvxM^1r&J!S6TD zU%2obTX=5#ij%orxvNX?q#h%)#0xr6R`geQ)l3&hr^Y&DbjgepZ@T#H+cIW%ZMPhf z+wT0m+qaGI#nceRe}pM`g)CqSVYjjCE?sCmn6XQ7TvEGQyRxL#t^9o@+t_ZGG)_IH z=OCxCI<2pN$Yh5>e*Gpn>`>ohp|*NQ;E73_TXdEUdS>C{w8tQ{Elj_!^M-Q2!-6%4pbH>j+rkzT1Ql}l&_#@a*lfA#^qEc@v zsfo_cQCY!6cE7o*>gv*%@6T218+_u>*K}$uTSrG9hCA3qCGb?!Cngsbbk6EQ zi~1=2{)9NahMEqh>)ofMysWGjwdn_UQ2~-*KGIw0cWjo4i8t6kCU}RhUZ7!UFiXy4 z`BdgeWSp3=IR*-a5sxz$*L9T6>3{X=)$E54Cs1PNE2#YvCv4Zg-8<9P-rmG%no?jP ztNz@RA9{RhoWFC^`f``P*Vdj03p2RWHE=g}DCPM_Im|Xdacnny>>;Jdfl@>_uT!V` z3>q|OcTkWIj)o%q6*rjBrtda`BZqH6&2vXhx_9qxTxT7>qKP0N%Cv33{i9KuoAT#n z6%~EPjOk9f+^T*1Nfe0r6^&29_+Fa#$UjdZ!tBYDC$`I%+k|UGMn=l)Yv+d{B>mNUUfKet}TJ|8bH?gs?$xqp6^JFgJK%Y9cu)JJF z0eyz9hWJ{;3mNvSCqZ35ln^V2Da>{vD`nf|7!85u3*hFZ(W zE?Ky68==V_(}3g72gh}KQt7Z9y1-Wrh-V~|*0pQbR#2y=KBGcTD0qJBYT=oEiNu-w||J06jLKylUNMQhLS}PHkv*AK-apmhkXXY zNtqs#$VS_Uv`Xy4@=(6GHZ>Iq|Ng z?a9QOsdsx19N0Q9FHddMC;@)5&(9mh6~;u&;+=-f6(_2DOD+Q^UI%@e?UNojZ^458 zz@FYJmjUPq)qNw+pO<`tM&d(%Y0&|j4bqn1*e>jXt%naE&WcBydxNYQ;u3fH^463T z9rlaT@EJ;hCkVA=FRl!w!DS0}nC4J?o#I+HzRsL!|A`zCUg8q9wR^B&mMfHH-M&43 z!h|NrbT+of=PTc>o9@h+Rp*->B`KxCu6Dp#=TrRoQ}NUz<2<`@ufG2Ny3?k$#gh}r z#=dq0)vS~t^h**E>=YyYB=eAn^xnz1}K~nc$zkV4Q7IfA*7YJd{ey2PC#pYwG|u~l=5iAV>d5TVcV{gC{DpH>%_FaqX@(9jA;p&! zBqeGO^uK`c#~JK$(G7!Y6xXa=8Jks_A+e(T$-rx3!p_*QPDbnMfx=yW_ z<71h!K>=zqor~DT;pYh@oyBrBzIXkf2(L8S1793s)YdR(d+z0SR+b+71!P$Nbbr6|QT?5~){ZTUz)UkqalDlh(lT!%1ll(XjP0*qb z;y|)u9gF-Uz0G%ncHtd{)ZF)D-NyQllZ|^Cut*fgjT`5kwsbj&br`L9F&Gn5pAmL5 z2Q4C%%2}-~Eh*VLaa;Qdlme@wuBZ4cLy5VuyZ1yj(mvA19gX9@5iObi%nCml6Jumr zPgxejnq0JGNlIf3#UrvEI@F#3C{lD-3Ju8Xw-UnMAe75y9{2VA^?-!UWD-{r$m9i< zhK6(#Nu3A{HFK&YTKaC^K5LQnsO1H>7+1DErwbwuB zvjoSMYHkv16qQD54yb3&aO8! ztz-Jz;$ol<7-hxa*s7DaD?p;&>l+Rjd7vSNXMFnfNo7RUbo+wOO}v29C)oKR_BO_| zXJ_CJDt!BP3mNQSEJjQAnb*;yRtMh@fb*umRQ@UF>6_wRcc)_$;+a6)C zw1=ms9m~7?>t~!wy&&XE?!5voq*LQMM!hL4>=p5N-l|n2Form$$6vilte&~zJn0t8 zvJxSk$LYIk*JNGYrf4U-8M{l!IRWBe8n2qV`X&Yt4jMf8A<0WqQ}YDNwhUc8Yv;*K zQ!*bm!h`B(&YTH#c5R4;oWG-8c_un~7ttx@%QGs7YIMfekgasw-U2d^3O0dYQ`6EU z0XjECs?gBTIJlllw~%b5xuQ^l2ZR(Y#hbCPusDI7&h*(1YSjE?Ooy^OtIbNED4yBP zn=8M4uDZ z2jCxZulP1#>{tczS{h|UM>)A|NCm99lZfn#vQIR@lduiL*D3s@vYCDnIt-9a^&VOH zsHTV`FIw+3pnVld5|~$h*6{gVl`^B$xT`-@m`b^5x4xzP7|X*>xLij-d#( z?Kw_7X<2IrVrnfyQ}Yhp5A+)|ChvKN!y@#53K zX5t6^34W%(VER7NftzXR0GfrnbLO;o7CP@7~u-tGMK_{79EzuLBJ*RAGi%(+2eosCwz zgZJ&*0u^CPI(6()K$CB;{{35s1Vd#Q<61fcRqBGNB`AhoGB22n_Q>R+=>YT<4Zl2l zXsv3+%ez@}@9;+~WOwSd3Pd$&51mz(&KWmnZ%ayw9)(Wz{`}?37Z#0Q*jH?}Xd7T1O;HN8rDEP~u;o>Ojz_H;z;ehpMYCcuTC3KrrOmSXza@yaYSn5y`Q`Pycj3E- zOv=e20~Qt*dJ*Qfmn-2{^Cs*Z_%U{@=Z_I^Pd*$!uuo-y>Rn>KKx38oc2`C5Ttl`$ z--4sg0a9QQTB7&n&9WCt4ii~Lj01B|cv3ZU%9Pd~9v*^}9o8YlDjgP&i z|LVUre8}9;_;`8XqwKM&%E|!Nr>a_?kM;HSYKYOR;4AZk1q|RxgyfE(^w_kiiKs`A zKUI{b`{2Nml?8p6hkL?0z!~&gd%YeFRha;4rrfDZm#rZoN~xY#SHG9))8~|7v7w&E zi=G?nd#gBS1*@xRXq?YhWF6}Rn^pid53VF!h}r`a*qoD|PR&?7V5uUarsm3`<`i1z zwEFqnAw}{aKXRjY$Bt4}Z9~;Hfd~#X-Od_tK=E{ubCHpgdxLvjG`F9tJTceAq?2HE z$dU4didnZ0F(@)sP=g~zv5P5t^|AVdP4qVXNtdMWy8EyhVNbM~Pc`IXMaf3UvXf*E zE?6HhL00R?_^I7ZUxD9JU%pge{9jB=jMvt!Ex;Kt`hKX6VCV^^C+D(r#IsypaLur| z&za!aUAQGU%sXaA%N8&81N$|Vpc^>w$Pvr%9>C_Zmm~2|MOr6y^YQTsO~>aQv$0`= zfYuUT6om)s3N~-v+>AK}=bl(j8BJ|0>A)D^Y!Hg!`#ymNJeHQJxz`9h28M>-adBVf z?V3Dkl7HS=`f_CN2MS>BTcGey_~zZ8I3Yo#538=H>a#<246*OmsWsY%FW$cE_wKa= z32k8@Vc$-7Dw-Xo9;Nh<82c#rQFvf{*R0)?df`V4|?zTAeelbCg128z$U;sHF%u-x?;XCY6# zSL(8e1IYQA0e9}-Pp6(RVx3%3l$)n_yF{auQgOag+1}U z-@u@1>+Ed(?u*LYtgnXi=lA8?_zD}UmR!U-W=F<_DaDLBK1y{i&d{Hs=+L2S-QAOZxv~>hvi4ox zgbf24tSbyu^zyE%fMV(*RJx@yu)lFzbuMZP_B$qXY z4gTY@+n{e*UYsgj89P}3H`3G3WNCKp-1*sy7pGv*#ceW!Y^UH1c!BDp$Kt0Vd&nX#Peh_`5gDvxd6Je&a`(NG`+s6H=lzdi0&cU8Hk{U1+bL6asXl zV2n1YyQX5=u{^Bb<<2FEwuD(q2>aZ~QPj4uhA9odx`W>qA?xWrJ?9JgFEBjmC+b3v zg0~eFZA5--*myKz2D=%iX)6)TSj~L;^mY(PMstT?DDA_>mwZFx$7Zhl!1`1{?9hwn zFY-8K0UN80Sa`1YU>GH7d>Hi2ZRQ+U!|W@|ZJM`e0WmnGWRvYl?@?wJG+x*S?BCx- zgbiqmVHpeq6*s2EWuxV;fG~6mJh?nczXlLJ)|hJ zTz9~egCAt|SNZe~IiyO2CS-fBB&{BJe$S*WTn;$eH0E(=@QmP`AOQBdua&J~5ZEdq zOtK4`b+$dVp@^e@xK232t;dfa&)R6yq}R})9Yoz#_VAQhjqJ5C_!BzH%li_^?`k|q zO>Kf+v6acJM~)n^wX(cuQ-A26{{q(+!sao& zGmGjIDDJ&dkXGbN{hxIVqi@0ftNHMkf20Kw&JI78kMh~z^3eb z8v1wli;e-21cBiURn3vvAEyJ*~v2-5uToX z%zu`EKG;bl5DM}$`N0i}F&>W*Ih%ib7QeC&Z^#dd-S__8f&xX9wN47r%J%IyH}h9r z)H!j+9CyEaX2#p3e8U#tJ&}>k1^4(==0bF(ddJZf1mQW&ZsP%!y;rYl;hWwJopqDa z11)Rl#b+?>+28}7c%BvhqQqvUX1)xxH%eD|nd>UWX*1}%D=o4XAv z8SYG~Lx&EUW5ygfygevL7JE;7h+JF%Ed?1$o4?XU)z@4ZNn_%(ygaEA)u{w3b;20R z!d27Ur&!2DcmPsQo-)NR%WXr$&o%QM9aC@L4m0__+lAI!Dbbc(qs1KPT8v7n$Bjli z7hLmW;iSe>)=!u^b!t?cLXRF~#@;^?qIOdq_kyd%r(iIBdKzi2y8gTCL09M%BWQwf z-Q_THhy*>!sKx0c7C&kZBpUK1J~DSfw8fk`c03~4`@7i329fLnhexNUIPT;t+5=1- zck)f|c&4^8>!}+(*yiEWr{=deTsSr=B|LJ*T>wT%N%^~XcY=PGbxDUUvM2os^QXr{ zwxl5oh@C-5fkR6Ak^;vv|NeoT*hpAm@48or5Hn`XD0>p#nF?GI9_^Cz%Wfk_j&#{L zIJ5uP>2TxpwG(-7W3wb zA{N_rT$ZM~LXDGDvu4diVm)^3SZMn=TxF>&mmx!j6f3Uv(4RT80}uEHugav{=*2lV zc+BS8^KPx$^Ab0|?KF4ov13dSw}_1og{Yv zf1lB#=d8NYLjQ4gc1KU;Zz=5g0rJDaKM5(5#eG+(qPJzfv(rCeNzw+bi|UU z(+-9Qb~hd5CgK&nWjoeaIHCwqI~-e(=kWxu>$<0v)#mf~(a z8Utws>^s|9&vbp*)z7D&rA$5-aeaIa<&7j^a{ht^`@7G2=$%j=hFgIosj|j$-n{N& z5QZ9k#s@1X&uwXAEZ)h1G7gZ1Xb2ISkx%JGgv!vb4**(7I$TRig1<7qb?escfJBJr zHhE651Y0|V3B#8YapFp>Vmw$)aXVQYdcK%?u0MFt0q)SW+&V(0XV0PXZ_3Lf>h;7! zsIIEe1l~njCYVIISKc?j$n@(QWT~On*LHUuaJl1j19S8JVVcBwlU=;t?T6}W-pxvI zY0PgSYdW{dYkZQvzG3Eq$RoO`YNya_?5z&H^6c`m?u?jQ7^GNI4<*#RL-~OL92f$f za|`jUNqwEC%bGRYu3Q;Ko4ZB7qiIB6+eM3%*ItYa85br;)a&`=fLr%vb807yeV90X zY3US4IfeQI!9_<7jaLa$fINzZ z%+*J4;CPC|kDjKu6iW#JMAuR1dt*g*um>j-(epi2CCPl{N_A{Y9c5&;P^#ENxtTqh zlZ9diZ9ViGux*7^3Vy2RKalwW{s*6 zbG?seRe|i&r%$EkWITFgqBcn-C^N~~#bp?_&Mi=??7u?F3<}Y31AjB25qPUpzi|7e z;Uw=`TGaJq*Qt014>{~#UjN;O6W@3QJz)!jE^Kh?Rif*E=tq?@061sb<;$1l0ok@| z%4cJ)x9)c!sEO=)1p~*6v9Zz~%SR*B5iNZZ65K<-UfuBXs%-!aC-8J``A2|+e;!(R z_h347uZ91H(__v!S>F>Bo%rO8XEwhat-Z$m(w0VxG^D7gC}s5-E(e%>uFQL={)H=7 zljuQhmL+Z4qtqsKWZIM4eH%1=)~_bKaJ$ z35hMVT28m=u>HBpcn)IgT>ZDY4PNeiafP_U$F6>fI&CF?woSQ?A5q zrlJ*D6$Yodq-1zxa67NCn|Cs68)i@iGcm#Rr2R-MD=R_cuK)HSWQ?Y1(3AC$D&Po=3>E{J;1mb1Q9T2+c^)?Cj1ei!GC=8Y_efzdKC#jk2+J2{V z?Kd}_Hj0gF&7jqoze=#Q-)@2h{#t}Ho0=XZQBBgq)2Cf{JAi_%oB@TsR`tj>Dj*6} zVdQ>8Q^iEEkWcP9YzUo%*uo;y<%)YR@fWf32opNgKJcjJ$F5uD^Ub}wa8p=F{HJld zDLTf2&YSCOX~33Jjqd2faB|~&N|#T(XS}%7^PwY0nyaOQ(eS@ZJ)bEHakUNcqL-@b zxrkZevNB()0(o{%a&lCyY6IE)sD{1N)Qoz&n`x-0g#gKt1)+rBK4|or&`?P>-H1im z5<7S9T(NTHCX%-Q@cIMbYujnmnK!<@ytH_4*xhbz+qF}t%k|Qd-1gL|TQ6SxJWrty z!O549rL3)^PluUw<)g1fX>DX31T17#KZ&77?LM_%R>u3vO50`2cF<;a@H0g~HJxMTDzh6SYsAn0K!v_H z_VkaH5kP@8nz+NY@H0An4G&YUCAB2VcxsdZTC!uj*B=jO^$8Sfi2St;Us z&6h8BxKxl%53VG5=mW>XqQ(@v|EeDi!Wu}YcF<8YOrX2`Yfu}TriQMfx0Theyp~3rb5N56dl1i#it{<`-vWBld6qLpM>VY z9o5p4q>_ZopO0D3pp9|k1d&7iLfPLPv|!c<02VZ*9Ko#? zwpq=!WEveVUBMZ$)}N6(qfKXgTJ8Mq`NgwgQJpzULH1f;yf!38$T(X;=b*>lRp?3R z3;UV;^XojHRb4Xg-AkkNdI%OWjMmqSc+4(J4Mt*+YB>7UP*7<07c;wD3NK%gM?=&O z7*pS!e5Z-5K6SKahzL6xMxR|=*n!0y6zq79b3n|CEAXrYbki`5vLA^)Ag&O7G!3Ia ziD~ZS8+0MKdv3YS_uVNXF54tK-0zs>p9!Kks@!(-F&vb;7(eN$z5k@swK4 z9eBg0yIb?59=^as`P01gMML@Xivuu=gx?4-?0hx*Z2vump(;uFrLs(lK~b50fL|qipK{NwDu&#?X{{rHE6z zFht-QQAaBh?5o@m%kdr_+B6sT(wuGnabNKLLAaab0=`FM?)0BqhlUm+rC4xjO_lXh z1|gV68ZGQa6hOzF`ed%)CvfH!kE`$XJ7$O&oEvR(V95|^vksyOKP(=eqKI-$Hm?u# zGykAtfB;@@iB|?iXx?)B`NJ*$n)|E;0m;eZm}%0}gJbCg)iWkGN6o%{G(Gj*m_m~Z zb@-5eE+4o3v(DhzPL-dN_DRoPWD?4ljsbP>o5de?zb-9p2~o+JO#_yX9a5qzIj5!N zl08E4N+))Jy=O{3rxuk?S_UzOFsA%j$G)KAd|Oz()L^`uscqJI2uO zP;c5nqIj{YXw1_sxOI@!sd z>nZ*HGVL%@si~=T4CoGZi8`R{bZ_>7_~*}`zox_Y2CA%)g+;CTH2gvLkB!|95W0T< zetS5n;`MJue$uP*I`rjEFOj$S7|X`6HUe5pN$Jj=+YN7KW{;teZg_=0vad~54q&tx zWRu(j(~EF=Q0F)0v~Ocqq}=8Xs7#p(on5nC3B~f7D{K!Qbp=D73=eND;G+YBhOU!% z2AA)DCUzL#PZ(0vacRM+=~Bw_mW4OL*0vYzfJ)Eam&$Tx)YANT<0nmau&Nz!Z}Bg$ zgvFgYb?V{8g)>nMS|Dd{fv>~E-@((<6Faf+<`E&Tqf6=k^nmpemPfM8VEHwL*PDp^ zB|I)kU&&xA=>|*H#M8z>va=Ti1P@tA-eN+?=BTKsvL3}>YCk%p2E!?YdiB!B37H)H(@?!41fd{hwWfm z(8F~JJ@!$622yos``yXNCo|N}l5aT0OPVz?=!=$c2LdoT2%x8_%g4!%WrX3HvWL$= znpyGiA_4O1O;HTXUM7y>zJ)DHm^+k}TT!9THWq_(5pi76;3T_b=HzO1~?#nH1E%WCno>+6xYAY$JPC>KhL`)rEYO*u0_A?WCGw^*&Ce9iSV+FN?C)jDY z*hsIE6?hUIJ(B0)Tw>kYldWY7qWO}TD_1fg6z2;QQHO0EdTzymz}~mDwe^A-)tfwd z`??u6R!H3nu)urgy!~c~;ZihF?UofWBd3BvQnnLbsPb$LrcRyZGASq*OKWXy?d<@3 zL~S-lR#5Ucj*fXrx#rcliR;TU+{8WZd4pIP5CCzBtow z|4goj%l(KuSbJTKL4j%G`UvY{H=%oDw33z%^djG!1oj9}m=zDdVXWXT^WH4>2}et{ zD|{8D%?J=>6%}1NC(GPuuJNMk4jS{Jq@=&oQK$`3dW3xQbR=9O?(_pWDu1(7Kl(Cr z7zweyYGcFRB|DLeu!C>HDbyPirfyv)`=U#+A-(((g0=B#(kxqe zNS7?>gKEj@nefL$3Rh%Y$AcFFm-k8xuF`P@5Cki+5a3xBA(=(Kwx&#ah)r@^9i0@z zHNk_T6W<-i0^SyX(W1qRgGL|QyLU5)s1u&qpiCc`nKm{(F_{hPP!FRd!U;skiUf5i z>s#1Tf&%Ra7R$zg-h1e5=z*dxZT4&&)>r+rld5947ut~82|tJ7SE~L)VXs~jSRNh? zJa-N$!Il7EK^vRdSzTg@>3LBa@aCzxbeRg-k|Sg;Nyl$TpjJd#l{sgyVP+vo}_$ABKZ~Ca`mG=p+9E$2f7sZ|*m$sahs)zb3}P zQQ0zo$%0dMLbpGcxc>b49JLZ&6?N!DUgj}vcW;1~w&xES`t~d?Et0N$|DIZC)+w!~ zNmFmt2TX>_!0JGv?}Wg2`_7#SnC)-}dO_xhImW_{IpOknf>AcDHo$=PV&O3GWh)k= zQ@j~c=ZP@;%iLEHNXdM{<(eTMA%S-7>HudEl$;CJc}RNKC%dnm*ppv~krjq1QK&7_Uk(|W5oXazDkEl0+>NM5vz15)(YjSO+cl{Dj*MMz zUQ;*GvFj-39t2&D0rBVtD>_Wo6sRlP60$p4LQC&~z#UF?@E-((^2Z`sYJ2>I^|B9Y->WK6*J3VHEcFL4_ZLJeDcN5 zz?~t>Ucxc3C`w!ZPIOp!_-jEBA)Dzx=*siRuT!22bhU%ym6yZ(1EdXMC*!x*1p^iq z6?rq4s0*dE%I$qBOjw)Xt&U&p)}LHKJ3=bNG&=k(oS$ftVcvrpW};Rz)yE$_=_bqt z>-#3AfaeK@w^ll_YtZ^40@40w4C+|xJ+V>vgYt& z#K1Jbjn(ViCAbL<%9Dfd1-2HIshA4;`alkMYX@eJi<@1&q?#5#WHJEUl(2nDSsm~-olMdsXJvrR z_9Y*HUc#?hoa~s74Jv$;=k}B5&LL0Nh8Vi`Di$>Cj(3kvx57|13Alewt6fBzsLHuT z?0S+AQs=6*KoDfzo8Z5@J+555coV)T8|?QGr?!(lamsh+&PnJ~sz>IT332-Vah+XI zM1IE{U>!uq49rXKmYYQ4i0JO%;9z-et#Q=UBsw$*f?8g}LEzq8Q>K74ir2>h@c_htFDpJur3nmwkPfsr3 zjthr#F{!GxAPzW5XedoZS9B}n<>d>RI(dTtpt|bzDg@TDXR&<N?^EHG*?&3v#~d!C})20 zbWcO7$9E)Td=6h~({H|^UW7<^OzIR4|KPFfSNmv1w_f}tOq!P~H1UK|tubR9kdUCx zJbk{wYYc#)T_F}wv^rzDj%coIO0dKZ%gzL_m}GtnR>`0x1e7bR_80|JuF<;Eowy`( zI!BZbt*Q|B*1i)R_)rQ zVQm$Lt&$!*hCjZSwG`;W*V%fte$>1xLzp791JayvhpiDUhJHy3GU&_Duy#z+*aa#K zfjNrKAo%uw_HbgE#tljo$6qg_ZtqM-@_&}NW+-)DfH1z{eZ8HTxlVNwtqiEpqH_YX zVrwg_m$~jVdD)cwU@e$fM2QSKoZQnKS*v*G?8{}PDTL`*f@P|vd%*w=!XqaAmGqv9 zOJdnUW~I+lXWb*J3eS$Pj?l+^O|jy|m1x`7!A32Ms3ux(uo6TLd-gbl2L+knBM zBS3S_haRvuf{{nwmywYf4^BYD7rjZ4W`f%$ukL$3_&saMn*k|VW2wxPwNe^P2z&$! z;lCD|wh%w?OJXnf1H+5ngca0E?S#XERwvo}6WhY7h$Y4t8U~oDQN*>ZW#0Kg zSGJVh^5q>+pM~j+&X*M{Rv0-Q6ZMIn>6a&`O~Nll?EPyn?evE0r~Et6Re0sdW}&5@ zC>aC`N2ZF1YWL$o`BH%B`3PI|92QjvVQo_N^~m7p(VCj_!b`tnhd#O%gy#GWw$V{hJ8AM~X5a_sv%)#R z*N~${-xo*G38?|hoU0CeiiwS#K;6%PEjjUMph{Hk2Kt&-QM8IJ%Ai8uqM{#N7cBO-2WeJipKM0joBN>_#9yFtOZhI?XC?8d^(>v~$T+xF0m$zKUy?2>4l6ms_LfX_bC1OKbaI;O zSl4KOG5&I+{XZs0(^GWNw`keE$L_Afg~JlwS)b--Ke^7GHv=S;R8*w!3*zdS!1-2L zqwUAInu&HW(Mv+w5a{85I6$>44VB(>dhaW*=SP1uAC=YcC^z?jd*|;|t3op!J_^IeL92Y> zfa}twziT_BlT`cN@W2O;)?;cjbn_m37qX>S#i?Fc2vJL0M|DOzhXHWOfSoO@tQ-{I zUV`EcZC79`(P6~QQ3g5*noG2^VtLl5fPekrLwi5HPwT81+-xho2&iU9Q}&1W;P%UC zNr+C_517L4+=N&KeLNno9P03Q5pXrTX9g_PWNkva1kC3m+{9Rx#6{rQA(N=k&RcpxKe^>6F>^MkoQRjps-^>~~YBvD~=&J}$EIr=DAk`6+2 zrD^&NUlE#)PekBIQ3`@$G7Wxv!THe<2SK}N;z<#Yf2YY#|8o19zF~lUQ`+#%j<%2v z)P?A>5Jn4oU~`yF%DBo(F=nPE<;nH@{2r{^V9k|EVvDf_Wam%&`>f3F(P4df)bgnE z5x?(M??2>S#l=*ksbfazjZj4o(fKVv}u{z~+!f$CURM)nK-em~Qr;xzVW zaZBW0{kF1(IPLn6L)lbSEISsai|nS3(q^*(9$q_l&M-SGoRWSeh|p@%__D=+^f7>C2vUvIQcrzUOIc=wUB)Tu*fB*h(B*AgKi^yL@V{|v z(ss1$?Z4Llc5ZQ(tt6;d+oD+2s@&m=wacF#z)NR-T3G0+WfOYe8Zj+Y_Q3U&1Mmc zpc8P@h&G(C6fyE?`e!0wQa|a9eak;SN`wMo?VAbRf5djI{kg>{MvUA zcn2d$x^Na^@8!8PlNd2#1hy)HKCk~_dgJ2G7cKRRI1VpJAiW#F4Cj9ED|D&<`|~hy z+kamAzkbM{-=6NYCt`-zpS|^e`K6%UKQ5+!U-sYM2ig9^|NZ-i{`q}4spS9iJ^%cX zLBD?muK&I=6|8c9f6RaXYC`MOKR@wdgG1H0Qz6<{u57XyzDoLZtFQjPhNd!lJCtpk zub+c#;`e}(amw8LZRp-|gd^?uh<-*JVy0l{NcK_tS|N2Wk(;?6PF^K;6-|GFp{^ObkSs5AA zRjr+V2Vaw->EtuD$$~I0A$`*brMzuDs^IB$hzDm2zgf~guX1(TpC=abz`Hud@SlL# z_{2OXVH?Nk?Z10>NAUFC?xcZ>QkHhfh%jAx)bZyPb{{Vj7M)(Ms?9DtQMK;OWp%=vrCIC^%9v z&H=C~wms$~K{ew8pTOt^EGH^IhL}2`@{VU>WY{wh!T6aoqn|9ay0%stcql82C#w}G z&*V6#nf(6L478j0mw^7g9DdFe@KdPu^jUC|jOVftfcq7EoANR^}_{S|()8As# zpFbw<|J>1eL7fpz)BVuc!gRi?)d|LUTkumheSK@nkPCcfj^2S->`}!BP6V^tv$Kb z>7P4OX?f3o$o~wj200_0^L;6@X0Ph1cl;=kn_{dZK#EZ%&iV&t4P?!>qY_~BO>~)w zfux|&NRogh|AJYE`fHk`U?;>MGtq8Z90y>9cl&3XcmJ>3=tRrbnFS;L^qMAEM&0oD z9XC{X$%N;R#bCN>V9*Q{Q0C6;b6PNbs`+bPP|@Dv0YMvJNX2mYdk8l6r5dr3YgNZbOr8e3Q>ubatJQWrgM6;Iod zfAx?qo79i>E2rKOos6ri3U;8&xCKo=y&5o?21eVy;r?%wj7;AV;(+IX5wSgxAmkGh zjbcuomk|{!*|&8%o@OCQ3j&LBV*3u1+(`xot>Dk4J9hK}cbgrUs>GDJo($0#&z&}R z7=83_Pmk|^?WL}BDEteOJyliAk-|kgGO!=IwC>^6goM$;=}BlRq;|U@o+jPCy{QI6 zHA+iM`(UFjJ42z$zK0ySPXBr6LbEQz4z^|15|2Xho5|7;qEVOX+n=Z3gP?pH{Ni z1^2p%$1KE9B$oNZ<{Vt|Gh64?-eCusG=Ew}v3qaEW6^=(1DiqhpIJODmI)}a8-A{< zXAGRw8^v)Rtu}6KaEXYRkAp@3!9uLv5n${ZOY`&xS2Bn6>nBM$r(dN?9kvL5rZ^Iw z0sPVj%tPC*{)4f9f}F8?-Kq_VjU57iqrIMK3mom&+35Od%=l9v1i`Ft4qH2R>{v0w zh>oQ+0@zB3RFm9+d+)4Z@C8*{Z^$2@{zUlu*jVVdviyYMn3}SoOOc#{e&o+QNW5UZ zh><{$PJ)WYmzI`pe$Kod%rZqoGttNgAs!LAlWc;j)(KKcZ|z!5=lu9hj62>{gv$~s zXsSS`MElLyM!D-B*3)Q1XK`EAxFRhVeixo6>J~%GuqUewSB|>JM%SZu@;OJlTQj6Pf(SHxGN`;3kuS563vvD z^dd3;NK6=^9dl*f*JXlkfx?;nEHdK!`5mGYH#1X;T0&SBAky3T=_&olrx4NJP;a5; zjK$3R-qN&{t5@Tj9sU6iSwnrP4s5QOR)!9x7?A0lrAKgHA>~T6Y}t~+ zQk=HqsP*kACN426NONtqsFi33v$}NV)Tw5WL940?6d<(6qj*xv52$^fAGc(-FjDvX z;#r2%l0JefH@3z=1crw`H!?uNf@|I?h`=pdl#i4ww6og;)c8N_y?I#A`4|74!3@zD zlzkXmk}XSiF+)rU?P;NfqEhyyRF;WRmJwM(6lqUUsO-s%NVF0{sFb}DrMe%-$jsOL ze*fOrbzj%rb$!1xlk|Bnuh)5<^L#$f^N5cHQ4&tE<@NI@-l!H76ae%ZJ$B)BP&`Ln zt^iyOBx!hMx#Q{_@zmLK3VaUf`s`RoQ4;WUS^`rbCBc`x`fQlrg+CA8O%WiS1YY@y z+G2_j!OCUb@R|{gf16-YJ8QF{y0)N&6w-38BdS}ADk#IA5mS!|2A2SJ1f->8@7AkV z(c$4pkT^<3oAJ5m0AI<<8&_ZR_1NzD-n`}KApZA1f+o)s^#0uH1grhDH*&X)d-V#@ zKUje<>7fvD%a;!Y>z7!1aSVY4qW&_zCqm2zr|^V$^K?ymdU2DQAf!8NGf&)<~B95{*Pp-DURh@qeC?Cm)xZWU;8~3c{6?A_S z(F-A8A+59iNMOcZ3ZJzkO;M<+-jrQ?>RR?Ki^ra{RaO2^`a(s1fF1c^#8RhXR7`2=Ovu1_e$jr!C$9`sl=32Uucd7qvG=^rQTgKNZN@(Ph#UscNyQ(i|9nN*I zYq&=-AiXq2W+3ZJj0ODMWz5SB z!tI7Ckj-!Xh_{b45>T%aZ4HB)!3(IoY}LaJqpc7Gm-7Fq|x61$Tt5?Bdd ze8{em|GXrpdKZfN5b=sUY-LSVyw(2L*hQN%m+Z_&>mk@8kI|^z3JiLd7T;`ugid{g ziRun(Bwiw!fV-Q#c-JYo#UM^fFg6pNb6|Pp&+oM6Bn_%O<6hkcBI^RyRoj^h={s`G z`HsESN5$&?VBk8i;w z!Gc-_Yp(8pdB|)OgO^fM!>`-3fLoBgs2OafPU2_@x*oqXMLLJ~%`MFe>#n|gs%kPl zA_54nbagEN^-Ko;G1Rk*;d`-`waHl3(D2sEeXKK8JqI+Fl-Z!_YT`&?=F+1IP z`W&i+ZPBEJ2ycjC5s^a`#6W@DkI2}sAmoeGyMy*leVSnO6v6}SKZRYao~VqwuM&Al z|J|?V@wkZ*BKCC{%kaHVU%wi8*9|!QrLy_-FY4P-&}{Nv0^+o!XC2cun?P1jflf3$ zLl#Rmx*ba^mY1HJK%F8QMQA*?clFFDx z)fj}qg{EeLqAp^@qfM?zTzJTrE?x{Q&LhltZ-MWo%OUYDPFFB3r3DWS(if}-T6H(ksL4mZsr&dvkhy!lvo~1sJ z%#mtBtRJZ~k&*{xOk3Uaa|}uK`Kn$se70%ttRsgHm(5zgHt|DM6(QeHBbsL=ywm;z z2LdT;6joNh>nGm2^kyhd8?=uO>vOeLj!cIc-*Z_+CTjkc9XpyAB!V9SVi-6*4&*rq zFUr>LHfYfC%bhTW!wONAi0Ey}TOUJr|4-qwK!QX`3yea7s!2P5@H`ZJB4>d)>-jRu zII08I#WTyx$gmpEZh^;$CLOwl1&N8X&+|T57;Uo`YC_4zXN#}}1zoUe$Hw1%*L}c% zW5r{7OvLs~OeCZk4owAp)vu!9L>>bA3rR1oP2_Bfg`<}i|G@PcJqMNbLIpuMQ%$_$ zNxQx@yNa$(VGV7I=-ZmGBgKyn^h$4Jkyx`L7HyRaD;~?C%!&2nuqI1JS{23qVtCIo zR@rcmkHe`S4W3uAAGC+{WZ=+u$fjjF*$kR6D1#o8n9@@+T1pWrIK5S=;ndoqQUZB0 zdR`@ITmCV+ReO5aC7eEeNK~a^z3HMQE7foJ82>%u%wg)SE>T;49My)nE*_Ue)XCRl z8T`rqFF*UO7_YU_?4bp_OEqa^I*9`naLcqHF)01w^!q?0jSp^I!6uR=CdUKR2DXDKFDfQ++3C zYhTTcoZ0=DQN%8*&I&SAzKPaQCd$^9WcShg05tyus{Sn}z%|tO-tTa8nOd7Uee6{+ z-g?TYOz%ssf+tYi>`uqU-s44{W^m`n2oydrWw?f zV2QG_E@ibb@N4Yl8q7u7+!LFcizBtfPClb@ie#oEA9LETs|J=j5_1FLpK;a)gdV%) z30Ozdyzzmg_9^6%cJ-BQUFv3ChhS-(UUDztMDd>q(9|Ayl_mh1c3N`=S}&-wzTda^ zc*U=(qN)9NH=8!Fp+f7{I~Io)y0D80ieZ=9QH_Z31!TGBmAD0W+caz2UwL#o7SCbx zja95EQWSG7EiAAh?6D_=&4NSpzDs+2k7~cTm(ZH*eVu(@%S2@sF6_8|yIdYUJ~Lw2 ziiWK9b@T0FTY*|b`M!wt4l+}c?1IOO zwj;aZ0+2uqNuyo@B}MSUvxFDf8_dUQN_iGsf;{Uo$U>uD7kc}TYx1$izvZa2Ix|Hv z1C+u1wZUAF;uRNcav$}GX64ZwFhye&pX>>VyPs`1v%50Ic z?vrXm#=8ATHaHHgq6WS?s~Przs#yal0wIHY{QkXoUQlKkXhd^pp(GP+f$|l)jBIJI zVJlDE)Vke4rt*cGP3h)0nGAUy3#H(eLlnwvO@q}lI2aDzsKl|}_X(4N5 zE6p~X2~Draun}Sq#VZak@Y1&`{7$ifI^pJNJypc1MPyMsE>!^b0rtBub>~=Imq2m5 zOHO^@;_XbsyW+mR-t@AFFHv%S{;avn7_`=jSZvbn zF#`45?;D7)C0sqil-J6+8hB|Bsm;aFz|gdj*M;@m z%4w+Y)j8yjKln`#Z8@d?ZjqVM!ZRRo7a0m+PO-2S(dFdqIX3q8A>~=eQZGZ7qm*8I z;>41KgZ;MuYGrTVYxpL$!GljsEh3Bj33!0jc?p(A0AfHa`6k#X9p3Ady4v_VB9)1y z_fRzG@}{P$_A$8MGdp(9)mEsZZMfAgQRm&Ojbj4wgPv0ErhnbGjvQv&(W#q0M0OhH zygya-n{Xy?KE|fF#3m%2ft&>}`6(}P$@!DQ!pL~z&C5eLX#m*_7cMRy^-G#D?Ko}B z%@U5s#a+hYT>bg#fjdfplqOeLTC&He%3mEWW$1)*yeEoINv%Y)4zGm{6xWao08(?e z_|@-^RI9JpEh7g3b8p^MI~n-!;N_{)^!<SRd>OYTq|EjWsFoG1ua1P9LlIiXTQDp)|efAvE9J5Idx!}zfpFG zE158Oj0>J@9*}{ED28EjrxOyS;Tse(&g{la^!CK9cefhFv&)*jghuO)seJfxGp*Ir z3(2iWan@C9J*}K0q9YKU;1E-#E?S)cVAUhW7k`*ad&>Oe7aP-i*63beai4Y#L)W9I zA^}phHT?}ltU9t#!X?K^h@p{T_AT0iq~eK9iGr!M7hT|)iBG;&20vToqh z?3=Q#q%-q0xQq9VJ7To7=dIIi17ldRgo7Wflf_1;QDq$Mi>z5a6CJ+{nulV5$rKj= zECgr-s|!{r%pJqUB?1DUspZ<8^mNFL{!t2ba#SSXoxx&F=6g_X{0)B@temdeqeqjW zT|0E_C_c6&eIu^R=t09X6S`f*_uSBIJfk5emo?=8(z`b2V(PT(jn2@T7FIqL2@*M4 zTvl~vW~R6v^P5Gft1)=6w3$P9xvLoKd|n>q1Bd>@fy>?N1?Pf~U4(gPr`LLFsyb7A zH74R41+E^nqyt*}C-=PdTc!;rv3OO(YEV5L!f8~Z9+ClCszSY@p4eRPRfB=zn@Z+e zz#tZ|8xx&rM#BFJbp@dz3STzsL>OqJ#plR0{1fxwL}(A34j za$@r0`fNaD<9}+EnPQ^LFT-=)@I^evQcbT=X{-AgOkKTdNU7hhJ!9XRoNBRezW3o{ z$7X-;XV@I+Al*I6_pw3x$xpHFs>qHsaM8M`V^(t1yU32?0Xg-%UfF-W>k4cH7HCB( zBzkQ8n+4NF}>yZ?Z@a;_Pz55!*S;3#~{3<Hmk=isa~f;$O?nWo2bB3K3?G z1BVW6v$h{@K*neyKA9E{|5lSk1`6;lK|+{&=9$qfMsr``$x7gihgHc|#U@ zcEtjUF%+s^4`+FLdWvlrj?qRYtJVR#?0wgTPPD_-$9|>wy+!&%jVqlmG>r7;q!6(x zguYW1g-4%xU%6;+n5kN_l&_7L`>_kq!%P zOia|fny`28UeZ0+W6zQ=UvAWD(}YJ8A}9%9%e(`!x*feG!^?se8#SWU{Nf&k9cpKt_lP=KHddP|{t8=tf^L6{7^?;n!3zrXzxNBv=P5L{4L3Qqm8{qnC9fQB zLnr#_?hc;Ikxg}B!#B}hU*K0Zs$4z%V#JZ^%IV^ah~mJwWTY1}9TczhP*n|ovDyf2 zk%+*>Bi{==Y>R>GrGR#9u$6SGZ7O9`qtQ0Gb|+ZxR_iX=DOp*{s4W$3xyCItokcwT zNEP@9pl~?GzM_YKMY;IxsIG7Z(v%7AV`$j(bIiQ;;K+YZcE)PAA%Fd0^EL2WnlTI) z9|dM7500T|tc-TVnAIVGp`)$bckS8W#|Qb`Fv(}=Ti152+qVzPk(T8~ws^0~tjwH| z%Do*QuRV@8Eq(UAN-E4g<3)fHozxAYNa)1B$y)pwvAkLL>U`ZS!xJX+WwCPJK3rau}ppOQ|Pi~+8Smf8T3osMMq)-?}d5*ba}o6T;tan)j{Apwor zbz;hR)g4A?Xu1}B|v_jh63}EBO61Dby?6mPd}F-Yyey@vRT3 z<}^1owq@T$)? zX!IDGBI0BRLzxf;1-N2UDNwLV_GOa}Hd?l0oz6x7s`6&0NoU?>uXY9j>Eq$)2hO|s zli2kkff$d#UZmk1!(vm$XRG-2g1F@l^_RrgA4}&qe7y;0ijUMGazgdx5{e+VG7M~Z z&)+}^N^L?hdigYLr(z*oa_eP<$Z8sQ9Ep~Xb42@TL=%#lNzj}sSD262h;n^E*-X)r zQn-vwjAqG{tor2j91sv)o&d$a#>-24(~b*-=|)S~OX}{?>g6x)wiQDH6*mT^O(io* zQwcMsmbTiwr|GtEU%CyzcMgMgVN5%2?@=SN8686laFP7#J9Nh^U%p(i5|PfOyLXea z;?FLj37K>M+-=Rk4hrHnKQ}t?j5L<;;cD-WhCe%$vS`^dmCZPN&3;XY8+2d?`Wl+h zTS8y-8;GixN`CI%#E`zANEi3;VmmRT{@Fd`m;&_(caUF!`^N>2M)84on{{k<7vwKj zVD?0j$@AY-<>*8|3M#PX!~BG;KPV2zd-J?RmSrbJ3B!{?G? zxmPc*XmbSju;~vr`S`TTePp8d$=I;X5P;pugZqrKhs{Vaxvf&rBaWg1tByDv( z$=TMz$|HPQ%OmM?U@Ts#bWDsxgBG*!k+j+FY}cpoME~K#-=El@A*OwTZ)&`pU02l> zhsxd~U$Ig}^F-scN7nn*Wv`jxD1;2fKZZnZ$5h%p4kTu^ryIn&CNYX3hW{n?{KN;Dy z-T`xfbM9oP8&x43G2Y=3-8ko(iszOs`w7yeF^^R*tvAlUw-c81LcAolUf>Gqn_pq( zhVr2Qo=WN&F{vbuOy0~ZNd|Wn0pO<+KLm)?ig)U^5cJX}S3p4K4>l`Q>&HRQnfDz@ zeNe%lSZ5iIpQzm`)2g7TXd`&m&eOG+f=R>4_14keWpLE8FxX|<)CAJpB+!j0T}dea zVBP@nPUvQaC0ry--=cFJb$U12(lzQpQM}J;@mDTRH2s?qy2RlR474t75LYK!18N&3 z2;Gb6#~?7g{lC|QbVZfWl{x~|kV0)OdX>$W;aHdmm8m~0+2eaM4hro4?JDUJWG+D~ zcCuLYTYUt_iN$bvh(lHrw&7(iuTkaiA0mmR=6|uC$_A2_ zTJI{!Vfyqz^ox1=sb&LEEw6cveUFPiynVVeJj#NQAAMM{surUIkSq27omFh@GVip z$(>a;_f}IAFbNJ;5#X!%*|SH4T-;y=Arnv_cc-&ldL#9`YR5SuFkjIyVA?$YT3-{uu}R2=FmLFpUQ6P>Zins8Y$7WsaZ?bY)+%*{ zX&t3Y_j(Z*(n$c4{oaPOnZbB9YSd`whwB;i;{U`GSVQnryrLyX4x}sE<+0Cltw?CC zNJm9Ewb^IeHdI6_Mi>Qq5NJ|BA-Zn=b@FCpA?^70jV#4YsmwiD&w~jIpP+C7F@m(A zXB91DY~f_b_nib90z8z4UY2p=$Ls|5WZ>KR^H4C8jFKU&v5Fjd8n-;w6uQ`M0zJx%Fo4Sf3N|N`mQ1#CxN>E+ZQqRNn3ZTEAv*BboBJs)5 zt2zjLKb^reT>Tz443lr{z zQFc_;@(mb1VCFxgAd$9M^1k&He_`bu)t0h;GBXW=S6y4r&Jarv8SHz>0c7g}FKHm= z7-$1`-c3O0c9~hpdd@|DyI__1B@L-V_1;Lu1Vox`VRWZC#FOon&Akn_f`N)nXK?

(qhZWAFzp8WubtNFm0kzxdW-idC!{p+BLy}@$SIs4jid2FQ~qub=@yi} z(@wkXI9i{QfqMQp2(If&jY^!g+^SNu+oJ!Ukf;3S4FpH1tIpuc98KT3qhV~j|I6K*}QJHw5o_U8Qfj%ipd5d1pTi7Mk)jX)nC4 z(XP7;9)Ors-n?USuqmDvB6`I&q5*i^T&*OKFU4Nn&pH-TV4;p%xi{UQ7Z(fotew?k z6fhX)uS~c?@nz__C>T;aX705%R5VEMjl#|n5HC{h-aPMFH%RkkKhkDgo*+9oeTJ_g zqUsl^PeT(epsT*-8;5!GtRU^!56NWGbPgUlo(M+R_`wb;;M?at$I9%?;+tKA-+Gd6 z%da5@(Ez>o_|B?3F~^TzB2$EA`xbNLRwO{`*Y49vk38mQjT>0Lq77qR8{WC&w*{4F zx9;6z3BykLXWEl?&rJ0~y(VaO`T5l!T8SC(rT0+SG5@M8LtAA+ z1|geTPh3lW@Suyd{>hX9pt_M8ksk75=`?B~QcGgn zd47n|ccvZ&ngUUgP_YiZUh%8*E7I9dlQSYapeo}yPm2oaRqfY=Z$nl4F zni3UraOK}t^etQVp3<#tb;nlSy0wIiY$mzyaJ+1TYm1UOa2bzcpy5o_PEY;r;xjy< z6siN|D?G8Tefv&bdL!MBlnBZI-AZay01&36g|9#KsqahqPC;fyNUP~)h&(O0N5k-u z^$Q;DROZfu2e%P7F}(jqjPS{pwgcfJ*pob!`%=`R$h z4et36D!)(4gVv6zCF~vXekb`66$w|v-;z8Q`>D`bXGelY3(g6fz=(l}Ec=wpmmgxP z_dDh#`0Vk1!6}+?$biJjbP1UnOUE`235u_t)}3V83FRskiWrdp@yCqvbEgn+qe7DQ zaTII2r;Gt#=ht0<>wbgSL+s4EcNe{rDE!Zz^SI?0L~$;nKV&dgR#u1JZg2C}1vRMwKQ%1?qa@}5Lu z+odL{9DYVD&%IWyc##K`drUvq=#EO8HU&7R@Q5bKa6(^S?|^iOR6}y8;ziC3=8{%R zgm=EL4pOy8k!>|~bSz!$_P_|nNSS1Sey2{yW}ok0wUy=z zIr*iJ0l7Ah_AnX{vFcN=V}&fjdri8B#q}Ko4^beXM+4|6oX}<2a5(%-8Rp~saclj7 z`t~xhRX>4e)^+4aGwZ3;j<4~lyEVd-%1c~gG@OCzX-eab;d9Y6$$%^Lud_dLR72b>TtOHj}2V zXA+^+Ic!?S_22C?(p+^%SG+fzot@Xka&E61NUWjzPI=i-zil8+!1@*%&KK4+kYRc3 zQjtwjvj0TaKKpa^w0}1G|J;sx6Q0zsPkFnya4fpGVPI_QJTju4BkQPo)E(!w9<^X= zYC7O}BU_mmi2u~C@tU8~#sB`_k>>LyQ##md;1(+|{YeaZv$B}qA@4U#6lI#(x`J_;ZdTYdJ00iYhPNPzJ4tF-^z9MW5uhs!-ZdJC$-H9o^@KY&aDp1xFO894J+;~#Dng>g3W1Sinof9Z5g zj0Ram;N@j=LryeUW6^&S4QGhqN?X}t|BKiJ8IEX5sy?ouGvNn}vRM3*@4@-W)G>B8 zrWVq2#fy`!Sm-eTmzrf+i6}BV7}G& zAezDHx;UfCKt`l+XAt^l1#lx`+5o~X&F}mXMgl=u34&yCR86rMHh3_c=smYwX3@$R zNBRlHn*;j~bu&6RCg=V%5u-ztncJ8Q?zKo#ND@^&PzUiq6;D0)E1<+vB5O!D#H={T zFfG^*i3*<}avr+b_J)q3Sf$ld8y$v*^`%BqIp~MI*2tE2E|eFFwuH{#U~ZZfDmHDp z_V8ibO`;Xmrtx)enZh_8_z#^rMQR+SX#xf$1yDuID0!mI0&1+!a%-IAGO-BUS^3H+ zj3jcHisb|U+uNvK%l#SLssBff?)zg;HvK+XXDYK*O-(mBf>WXXTOD>o3$Lb4jvRE< z?i;MOl4otwnvT{E^hPW`rBz_qiL(aSN3z}VENdW5P4r%cW_ip7rxr6sQK8FlEnd#q z-0MgC!=s5z(#bADKUi^VrxEMON+QmxE_+i^fr6+p3X}nfdtdxWm_!G1fogzwfnm

{XOm^S=SQPrFQk(-~1a;*X}w|Muoqr}EGYNs>Yd2}TL=J6De zey-E&vlY2(y{B(czhkSa^JoG2po*nx{j+E(4TMJ~+W1dGC*?&U(*{*!o+dZ5tK>PV z9;C%bZ^yHV4x@Q{R4eCUYU<>ynI!5mKWHE?lt$y;mZcjRP9Y|Pf*Xv;-H40hX7-iL z`4E4ERkglsFCzm13u!4kC+?OZN1b5f_`i{T#H|m=c+^dH&t-G)3jv3?4F(@ID9nmP1DP}M# zv5sg=FR}8v?!MO8@$=#QRlo>J^^&(}Y||lma^_7{BuDt;s9iVLA8$^tpOvo7(Q6GZ zkb_IrtQVV_`bjAr68dfTu9RkK{Fs~2#y-V@X3=rtXVQsOU+rBLm5@|`ddvQ|5x#!q z={=R=O~22;E!HWTaSXPU(YiffqO4kri_)btd{c!J_Ik6A_xn~dQoZ-)25Y#S-yP0K zw-hkFw1AThiZ;j;KA2|hZl{Nh8S`UZZRPpKkF|_I3#b!%fdDFBxpVifEj0kG`(2{g zkr!{@K3%LjWN!-vva0;MWiOA0gqW062mM7bqf>g&CsSnl0VIaWa8I$I6QYSfZhLPZ z=N#eXXVo1lN%`kJJuh|&cbIqW+O=eA#>p0VeYd=>XVEagqJP`$EK@K{VH|)wPrFt2 z7Z#7g_aTS26p!T4zd<}UEBwE_*0&*lpMWo<md(EZ-gvk! zRGu^aSYx;qCSvJ3UFV@lO-d)(q2fHs4CQINzNvNI1+i`bjX~u4&7!|aJ6jBr!THF@ z&#t&nHwOVLg|_k-#z=ULkQQkEP$Y{nN2lw|n$?;5o2k(u8qowrxb20T((L%0D+srI zJYHr;v;|HI)F^_avT|?;Uwtch2%=TxHf_{5han=DakJzkmC-HQX(&j;8DT}Ld9M-C zv(Ik@Y_Y*`v`f@XbMs_U{YMkdqrR>g`|jna(Ix6i)$eJ3{N)$L-)8lg()!58Za=sx z{a`rGJ7e?QJ9FF4oYkP^jF#2m5vE;ZrfhfUx^(S~U$(bt`ct;Tw@m|&wElHE6Lud3 zU;k*QI`@|k9??FfgO&u8?e8_G+s{P-hrzjb1d4HUpFx8%UVk*_SxsZ@Y9!e89&Q3A zm~^gJkNHH?k&6m`dz4R3T`Ja5tj?j&Ss&XJ**@#c@kBo3D#^x}y)C7I(Y!Y-Rq5_ipD=C8qrO=RONYnVe4XA2v+(H*dHAVaQ0=^3XeZJcny(X}Z2o#}R|>I#=J$P+CCxJ1lwe zM2y~TV(s8p+c@?J18L&!1EvLxo_|ucPoF7-in#Y5{0+{nUd76|@!S<-2*X$#Z6l^= zkE?M}Vd}y8+KOXsO7mSy`zf{4ICQhfFD__xfBIJ@K{;fhhrOP|NS*nFF(BcamX1KC zOdxyOyy0vRgj~Xk>s?!zmlf{E?r8uq^|MUOLiQf-m<>LkRMqj#+qe3pZP%k`%9s~+ zvRMZPt^i(zBZfDMGRrQ|g4~TLAuR|2LTR64YWCgN&t}ggSr-`28&K8`|fn|?BA|&p7Z|s z>Ng9YKaa$^t)Io|o6qwpjC+q7Wga})FY&`|N|&8}e#=ave0+Sgs*1bUAP`Hve*KK2 zJI~7X@s?rEZhgCTyR`5f_4qaXy`PxD7AsZeFGSJcwPAxgN;qGV(Ga}mLqUZdZUuRf zjXinw%Ejo|DBV38f#d$WT5=Zkebzu>^m{=0e)+C#2&-UzmcDWH5GCgk6Yjr@jXxDP zv-Rji&A#^2zyYr=OpltpyMGjK#{BuR@s#3ya{xpxmU#5+I$*$bIGJ47_U5JH(o3Ap z>o><_KQ~gBv}m%I({)x@$`*q3SqlY4IWH_ayeK<^q>2 zBbnLP1xp1-kd+NZ9n*3tQV?lgU3k7XQ06T(hZPsU>oSiFq3YV~+91l862Q-iE zW50g$C-&nh8j^9j$o%K6> zxT4OR=8bq$1V%2)&ijgzCdx7?$Ho$R7_jDrhTfYKg$cMFcY~*laBK4}L9B57`dj8L ztYee8)GjeL-i1Gf%kgL+EZ-xf(0eV?)6?lND0TMu2%g!YHh}K2Pzuuxr6g8^_TM*S zSi(&v8inx&=TTZF;%2_Ryz$pJqx}8ap$f~b7aU>+l&$xcEt=+;K-@{|Y+xCDSvz;e z4EtumF6Vg>GXmFT!1{IPTAoYQjVZK!rpid!IXeOQ=kDe!;yXX#e^5;|5mD-E1DVM4oryP#dQC zmL05vsr$ovOFz=*cv$mzoH=0?wR75jed#ka8s#b6mJWKuz+W0C`@>k>dA)q74qNZ8 ztz4jfnzdnL!!e7#=faRTdn6eB&y zObi5Xn;F@AYtg#kB5~hT%0;ysMrxv?PZRo8e_}!ed9Lpf;PyRAZkQbCP+0l){VuO> zT2CDqy((nqL&zk2;!mOo+f2`aRatJs|6F`rleh*k%{V8PaY-?SWP6B9{H(Wxjoa4) z{bT0B2P9bi@|)PD^;_&~7;Q1aM-xOlK0H?mf9N)jsk;Zb7^M3aa2%oOFpu0v4eIV` zyGwJn_~Y~$Fd)4oK>Lk_Oc=Bt4#gc&g?b2x)#aU=tf3VZLvl+e{*lIk_VD2$lHTM5 z7d6YoIK)DSc>m#4s>Uy3C(fC3gdtRSyOsVY{Kfnz>&CXX#x9zB0f0`FQbphDzHPM4 z;wTh78_s%GJ?rIDwzt`}`&a$PnZt&%(4$Iv>5vGwaeTRZoXkdwBUegDOze;oeDr8Z zLQ%kpc0RkI|IrAIhhcGNA~H3SYKjg;+zEb7Zv7p_%1Z4zH8p_VkCP_>%&e zWk>X2{I4G4rz3mJXOa64n(J*{Lk3WeczGV;ly~ip{YB;z3JEY0+SmF%%oa##zX(^e zGK+Z`M>eJSgDE<)=Sx-QkFB5&6sg1=}z?2t|)b3-~3=3a|$~370nvW`Ic; z6T3B00nUJ~(qRUAwpM;3>Z6&!5kCue`sn z%lWsdeT=L<6=RlC{vZEOS+Pni@eTv@cQ-Z|K3KFk++_R;rr9)tKACkU%dDz z+Wf9F@U}4p-J?}Gjq+$rwHu_dq7Ub%{@^n zrsEt;%{>_;n^!R6>)TSulx^0>e7)^3x;1Aue?wJ$c-+~ZcZ-gzjuw#wc@vw5RzNa# zXHx^rBqLn@=+m1Bw|MEtI?u@Aqek_Sq|++vfaI5gzaXGIJ*jV-oY8HOwg*BUC)?a- zG4I^7XP&7+n0`;L`=-q?)Kpg=Jb3Wjm8H+vN9V<977L?jQnjn5+Dw1$&$A}d)nE&RC zXKmTRp=m3Q&EtfptIj3XnQXc0$&TsIHS}(kVvR_KWo5dgdz`(p1!9pyv)o>2`V@_+ zytl-7YYBC+<_T1<@fjrmFZblCXeBmFWjS75dfY z0}fs*@%Z{y)&!AHt+A8=hT~3kpV|61a3$}~ZqEnqb3=h-2Ddcy1-a8L3bTGWylXSk zm`q|Z*3OH{-cUdnA6SvEhk2s2+r6D&5xsl#xP0`(%bqT0-fUq1EQl318agre{HUsK zMwBL{@J~r3n+zq?QEDXhyS*FuPX8Vd0+RdRzFosVIzak}vz<3wuvw6xT5aT;IY z9iP3NGv&~W4P03IIaz-);~bKV?19}?i2 zS~&Cb&5wSMPs0&HhyTY4nc8sV&o6Ol<)Pub%M}zl_a}icBF&8l#oYI9X2&3T!R&4Q zzGq*R-&>OY3^qS*Wj>gMggpJ=$zcp8PEAeK|K*o;*+pmSYOB?$!q(cIX^Zlylu)S0 z)0AW2WdH8n$D>BeVuV9q`=aX7Z6UYV9L?t22icRn$!_yR>VkhP1e+s;b%95RQ}$rO+dpq)w49$*D%fyFAD*sTvi0*WUyxeiC_nG-?~di zu>IB4{yI7abDn_~uP|!w^kY=r8!-c-L_R+M+wQ17b5YAllDi=P%uHklG3wVtZcd3w^qDIrvSb zhCOp^stSAtc%O06`S3cqg;B!dxjxH)JPbK~^s{%jf?h z91-UP!zOpgF$_1-S+H=T6Mfd9uPvA<8uSqrQ+xb7qVP{u~FP6{-R9kbGo$WUa z&$5It-i>kY3Jjtc5oMgaF9UeAhOu`JsLTSZ?#&?boGrUC>eESUpR{|Wn_FpF`q)#Z z_^=0yK}4TE*SnU%fSp66a+25ySUST9`4wd^BP*M;KCsBc4SwaY?RmIj`E?{3UV=$pb%t+ z#axUW=*_Z{uU^#w7r|e4=PdV%>~HR0S$52=BrIeIlT&<_!HndvVdih#T&&ZpAD#QGG8(axD)2T}Ltd+4J zVN@f=4^rn%qd*(NVJ+1lR7!ut0bk$5xLb?plu94)tgFK4)u>2R+P9w{d*jwEJ*c#y zUP-iV9zxS*YIGNrj+*&aNq){>X=&Nyf$=C~up=xc@7H5BU(I8uhVr^fFgC+@%!pO_ z&4C0u7A?=ZWC~YO@Nm6IYV6XEjWdYE0T<6%?hCY4?6!et4(XuzGK3NiFE(-M+??{lNa6L=*2gVAc;2c`{?cdLK;j26j zJjzV_#V^22GLPu&cs{yi{d2#t-e+7#1?3YY`8|8|P=iIKhnEd0kb-31WTd;wSH>Ks z+v{#XGl*RE+2_u{Cxw6}@CTJ%TqhT)%fYz=HWvwzA@~2weVh8MnO#)>_54$!x#qhA z2>pmUrcZ`ld{hhbdPLsTFJ6j0$E3PnLk&p+(sRt`tFQmN3JQnTbeiJ&&wu{&Zx@o+ zatr_cy3n6s^WSePJYV;J`SDJaU1d{ToBZ>4)D)$m1kuWZJWl$<(1|-7Q3*-F3VQ$~ zQvpzQH?ICx6&_kYvdH*yjwvXN(}Th{W+u({iX;YgOPW>I`O9zLQ)v3{AO7-vh0fpq z>!JPgZH47OANo%k#2;I={lEP9*B29J80L%eWkF^D=x&)Ayfy`|@oCh1Uyzy7`}% z$)CqG`)0wve(cM?CjEc;#~1bo1^V)Js0WONB~0Vs1kSTjqo#jZ8?UXF)Q7n*KmE_Y zw9xwhpQXb8r`N&%i)%RLC$d>`nKquB7iW)5((Za-j9X4Y?kf0~o?}$&S5;-BqW{^S z-l?RkJCPwjtU)wN8oC+XBN!)rs>izR|GxgODf~OOjMgBQDR=2Idrl3_1%a?hV?I3D zfi-=X2E*$A!kS+e3ljR}>*1_r$cAf3+&XBjJ{@it7DTf`pAjPxj{Nye{STdXA#6Lx|9hPP&Ag9TFQ2TBe7-8}gLF?m~OOWkQF^|vHVK{nA>_gr!##i!&xU-n02C2JYGXKA#z+{QbehQl-R4<)Y2+m* z7f~g(C$-Sk`}Zv=9N(6g?~I6W+#X<(foYaFAaMxjQ!ek?z1u`*TcgjPe8F9^fG=P7 z3bs$`d~3x6_Yiqx^yr>6rJKfj`2skpM5YcuszpkEtFXNblGWq*uY^JOl|ZLUjY(u9 zx2z;pJpQ4~JZ|20IX9QzCoupK8Jw^jrXC04*??&jdqUlj{MlzGkb$7$*drciQjGAR zPDPnS80+#$H|eg+qDi83Y}fjG{AnXzVIO@ve_1jL3f}8TudQ!S_SO3MdVF%nJZG^9 zhUWC!wad=5sGuMS8z~^Hd-38cBtXVO&!1~|zu?m=1mvRhAlEy&>O9`TWR`(Ubq*yD zQ+1TBB1lPL{!!`yM`qk~80&FH_f!fkG}_elcDeM?M3W)k|2TZi7|Yr26@KSF6m|r& zDmj@)hfAOC-IHIRAwh$xo-Q^0ql8m$D=SS%E-S9~I@m5pi7^?x+4%5Y^>^1|(ElDF zFSR+$9Wsw@NlBzN#V7Do&|>BUD|H$^gQ7qbONz>NJC4wKRZ}#!P7mbpdQOiXJ(QPT zPeOB9T9G^3guCO-8O?Ebi3d5#hK*{iI`wp)>vmfpYnDCB&>nEM+&9~LsLn`m-xQ56 zi|Kvczmu>v?V;aJhTq6~;iL^`WC8b>g~EFFb9rO%SPrgC zHjif}2|5>*@LWVs;`R6=LFvL67VQu|m!dRs#)&V#vJ{s0%YP{Bc>~^H8Yzrf?2R8< zw>}K;kZ)i;6rHe~@)W$j*ixlcx9BunN)%<+t&HdT#p_Ow`8XnY|NgsLdEfe9c46eb zf5i+1Rop{<7HqgwZa#9!nb((^8Q)j#r6VtgZIz985k}o=y?RX&+yIy`jzM@ewbid0 z`v)$5nyQFqfIrA)G>TEQ#fOs>tn7V7Qr1vkp157c;H()emS#hy>uuVyCHuQBtF4Tp zP&fvxENCjP&QIOQw}&NWh|t8j;7ZLtx;6WJsTYzr{7+zbgv`P9S@7=2@E4SMv1zf` z6JgwT)#d>A4%~YbG0(ktV|Foxk*+v|LfssrB%8_5&NgneBv}A3XUa_>dSsfff7CK| zKHsB0(;ZX&{DeR4v6RPlw>_a|+t{l)cVT`#;?{mSoFg+K_N9J(W!7f~XO4`OXM>Ym7VNZr}nf3W>lfOPwlg>1;iFI~L1ZQIt_y<87_ zzf>%;I6(z?0%a7<1V&VMO9l@bBzg%SccdIqK)9)CX;Hrx)gK!Vt)bRGUFhhA*oDN= zH~-`4l{2Wl7_a2pg94MEmqDH3$X6Y5B8ADK8870uUguUYL{SHLv`s0wd=#Pnj&-+( z`LZGq-0ZO&V16=^!7Vm_Zcz z_bWzue1xrN8tGP%ZPIVU9p|Tog-7xD5}9TdeHV&@gJ$x8xQ@Nnhx!3uQ7*G()L+xt zvGW$#MHVGLv+FM%okgQX4tLw!5x`Ru>D?u#`q!b{_4C2{aBVCm67FxW_7a%`rMi)u zP%j7r+Lq0!+=hy=Gk3A$MPrm1Ciyq5HaCpyVm#-7!NZlNbbG7D6@W zJxOU@4j(|1HUR0?{`9prf zY76yiF>1-(oV2l@WSu^mR96OIhdv87bd-Y^G{K#bjY8hACVgtI7c9rd#0TP{JT|#U z{pXCf_#7~$^NJEU8k@CPGm~u)j0R`BHJjK8kE{< zIZN0Nj*f*M1#t;04DS4 z8Q-7@t@0_cdAW*jWz(Ac^wA@|=k~BwW|5LZ#2z@0wKa3gCc??3ET5=zT=gbiL-reW z>hEUd>EXM;aX)=E_B(j+7<~4w@^0_ECIcE1-j>TPtm}GzZ)0|oC zg>G(EP#8TG45bc1V+n%b0K`OxFZi{77 zPs&vM2F*=X8!_Te3}ZI#Ps^11mao^ZKNSa7>ydUlz`JhHMBWo;edq<}orKorZg|LU zk*I5o>M_Q&>{7D<#mG8nq0F=tA?xCW3)QyXdTB0)Cy}ym&Y~%jZ@;w1n;<;Evv$=E zN6LY4NNWO@ZBf`uGBg-ez}f+Cm*DrgY_7e1#~i`Lk>Fv7c36nR9h#MwUIn&9d&zm* zK~DeM^E-UHUjDCh=g(EZ1@}^}azp874U+DLJ3ZuCrVPw+PeU%JvA+dBJ-Ug^$9?r` zc80BK`R?-{Q*6=ziM`9O0~I01x)E~)I1g31A=s98xsw~oV!Y{+kM9@ZlallOzn;^= zFNK6+YR0IiAnSL+JAR6q4`*dDBEUxzKFk17@%96&fb1c6+RNsxIM;%e!}{%)!&IKT z);PNU{nfdD{jvMfoP1zIsU#zrJ>KocACBi@L|1Yy-&%?c1cS0v5K;oLI`I@d^)SLd zp&)yXd1(pw-a@cBsCoQM4T<^$LlnvY)?L6P^<@3nZX2UI=}+HP(~lyl6RelOW57?6 zwTo;F#ft^xOTUu|uq|MmVd!#E5}@1BA-2AK{dxxD2SqmnE;E5tWx(t90QebnfvJ#r zh`fy`j5!LYh5p__bDtoe;KNtBxkUi~g)^g?7xQR*4Ou?t=ZY* zJWZp|V*l*?=^9t8sI)o2$cQOuI7hB}djyehB*JjgjlY;OOb)6~H1K(PkNk2z`Iq-?BK}nz~Kf%dI?w}7xBPq&=QDq0WG*CEgLa+8@o*Z?`Q1czTcZV>{K)OVSR!o~R zC6fj%(SKp>P+9nxUa`2-r=Sam9$0ovbT%peT=z3N$10yZh^T*Wr)R%O{ku|G_&^lO zDjhnwmOX}lYM*m(_@ZAp15|VQ9pWgS|L!(@EFv3A4uEitC84rfltab$3?m55$UDSe zD8jz)5+9Lv=5ijyQ_V>&f&#<%1uV~g+Gogadyyvq{hMv55L;T#s5=#ip`Jm1$Gg7l z2&Wm%W}6HXeT!ETmG_tv1kkt`A;=7PI47+#Hf#pDl9HoV-Nz-O0HPELMag$iB- z8WDxqdSt)~#FICGRJS|VfkF<*>7tGL1s3 z;z~ys<*QqIN%N&$dO#HUZZS6{jRI`?3gd$<6fUes1ED>BL-Sj_vjwK75W?{Lr_#$87ygQr zGTc63)kGMI{xc5M@&=8w+t-{em_>)38j2D&*S@HP}(ls=^{bEI3kvFp$C;1#9V zB}6z-#PRTU%XxFMRu~6dx$Z1;T)*^Gz*zM2B1$Vfi{C>Pc0J|j^uy?uuPTW+mc@Y` zQE6VbKVebk1J2wb*pQ13(B=Wy{T61RGa~vHJcH@OyB)B743*58ojLT?7V;JjVNDK4 z_L>1pB%2)7)8sy<2&K!-Z_-v2t)e+ekqIMcK_ov3qnffSMf$Ozwln>nM;;|=p7P>Z z7eh4VP!ohQQEcB0W?^PSI>wQbo#hHCnKIZxiYiW1rcIMfN4Y}>hxhJ1b-*w35V&4S zP9~Xlna|NO)N7WDl*pu}M{rM28#XNW=viKNCZccIR!}C>>uO$kQj?7EsU}4B^*Gzd zFmz$r!4t=i53#?|=`8XZ!eUayNr>B_X-d3#Cx#Dk3-Rw)tmSNdMxEe~j}ADH%W+k8 z^~pr(c-kwDu?==;<}|v;)d)fKZo>%-Pl%_w%-4vy9nph3GM6Y!PN-Ch-Cih1c}aZk zECU!|=i11{G@p5G+$ir=GO{rf>+$J_YsX1ie&Jj36Ln6SduivoGlvI~NRJQ|6>8b} zOEmu6PkGg?S>8-rcmdafO}(amYwQ%y@DwV*$+K6#@d6|Bwn`k!BojxdOS9v1Hl?{$ zzPKAgy5q=S5vVP@nq4r2Dc>?!QhTwZHZTapp3R_}#+^u5vPq{oGtH4+pS-p=hsGmA z2ET$T85YkXP?<4KgPPLd;W%?<;Rx1%PvO3IEPY2#D$(`A@i^rr(KDpfe$eF-bA!!H zg)}2hip)yfzKcDE$>|JW|YuHc+^55L`b>7Mm#T`{hKN#{Pl>(@mynhtnabSVystk(jNfSb_o zrF+m`hcYL&-Mk&OXR=>j>8On}jXH`)q`)a-zAy+adQdHR>(0vPGTH6eT%BzV_829W z4flIl;h?i^ZMAeRX5l93+Zot!bPq-tMZw^cM4mV@srMh0@uCJq(`vT80-LogcFHlf zc?mMG{=NY)|3g;CM)#EH)92WkAcWZ&;Z;-vvS9HqQhU9(avg9BUhkpYdok zdi(rDdI>g!MsT*-Zi;1{m%(2<^)O7}f4Hqg*|wmf5s zAbb3ok zWA(ZEgy3E?sgqv+A;la;g)%9Tgmdi4Kc_?_Pfr2t2(Xm7)6oLyVQ`L8(#yAVb)d*Q zq}WCzIs?c^C`$}w<5AkM08nhv$-9&QK|8Y=r80>kBzz)8oH}>`x>Pe#8(>vKaw9`p zK_nVe8*4AtS~%d^wcTcsr)Ol?D%e%HU)FVrf7|=nz!?+h9ScX#mfQhhm$1Mf0^zgH zCdyMH^CamgrjdsVY|2`fxoVqv;`IHe7JT%TdIW{D1T#KiBw}h0goOvjdgd>j4bLos zrURo_>1%2AtUHs=GN+@c;+SX6wHRljgJO#orxK3=Qy>ZLGkyAWGTIV8CUIflyxP9u z2#iQ{j4-buuJ>m5aZpPxiM=gDCXOLpGk@M1$3I}s2rgH9(C#QSjG9#B5y?(cU7%@) z!VLv}=#4olTPevt`S1hx$^!yj`t>uzinoWT*hy-HXD9Ie z?INeLJnK-qWi}oFM<@-#0YMvAPq?fLYoTlm*vz2DKx7jR;6o{{#^Cnl2u#gBe#rSt zIv1FnUR{vZ$?T+47=D&)SJURz`$QE=bb3}(`C!0?XZ0&)U!$6zgW?>$4mxycW zmTSAhp9v-b9#!*`JD3aaanPeGOpZ9p2LC7ykU(-o&-qCADEnd(>)0wXR`K3y4=kE* zk3P%`c769e45VIzj3 z2Ju-&1kOPDhd5^0-N$UZs)y@a3|WvjL=v$3o=;O%Lk?{q9I zlfX?pX*G5A)bJXLz(K1ozT%OV=exZ^V~L;v_mmlQ^W#_HY(C24<7xU3HA(3aD41qX zuNwRpN~%7n27eWKpFzl0YnuH08A2bCJIgJtK-Z1LJ+aSCV#pYDCRK%hq7};WtgD`# ztR-g<+0pgr>Z+>ziref5m5v=9Z=3j1mT8i=)$pv12y@QnOpOy3AiW#&+eQz=2(K0~mKix0wV<>Ys;NMhukHaeD$5)c zKeO3@GlvKw5k(pyRI`FIRaL$k(6;UFyE!y!YGUV?c5v(in}bkmC)S=M8+Na$$lXvq zw)*+t9o8r53d^2MHkpGZj_pZ1T<#|8>S~_p2hFQIma1t?+4aF4MlKDNRGCnURyRNV z?lPD6(hx#u*@Ea2BgBE`!zfokX^aU@2pW=_yJ6f_a>xE)c1M>p))k)n&g4>Pis(v~ zBtd=7`z6cyXo(A=nw8X)dillh-t}?0k`4=`J>zH{38%Aw$=h=gNV#l(mk;*7;rkyQ zjDC+eBz-b^2l@#j5H#G#5_AW zoCKqnqsR9Ivx7n+E;CIBdLjrKK!$4(bm_Zw^ucP}NR${q3%4qEX@@ zK20zuaONpQZpr;9O30#mBY0H@c;bd70}_ZzG~diPAt;_$Y5ppTCw+{lqR#ocZ|H&y z5E&47=?MG`O##08k$AkSs=i8eRjgU%@A^2lK=LTU!gNxcdv$YP2@NY-6p=J2ql|WTMwBfp9V4TNk~9?!M3QWh(omu76fzo=>UrNeo#TI= z=jru-{f(nvzu))!`QGEYulu^LR#a>nb@ri!eTyc`_ck57wPC~d$C$;Ww^KWlaA&G* z>yDWt8!qsOyQLQP;B}~r-JNvr##$4!UhD7hNg8_mveSm|Us}w`FD{LWI$PJZ^kd|x zsOJgA-wK1y-glql>iM1cCn^r~bU-M7k{ZX76qGqStc4r=n1wryp<-6Sb=QN^f2TI@W_MP1;@Y)C$wa$l@-;W+k||B7YLp0i ztbr;i4hju)e4<2e`{Bce`O(F5kH@2LR&Zti`L&N#6=HS4q&zG%8$1mGg)3Vea6#a)7)~M!wN%t&Ash|sN3rxaz@kF~q^s^`>q7IRuuMwIM0SaEhY z4eRhnsht0I>=tUaZNUb=AEClQv=z9?K@o!y++gR+JEFYM1*0$~?r~8aev@O%*>+>J z*pa79va;$0$gN~m&-sWb4qm-`!B^rz;o#ieOsC8!qFN7KzVLXwGE#R}05FhJ4i0O> zz4Dx>ccD81jtzwPsc`4b*V{{!xgC9?R_E_!n!LNFwTjNYB6avKcNuQy^-#2Y{PxzR zi6DjwcsJ`C8ukvJ-m`g6=rC#{y1jY#W@6jGWOQ7EziMFrJbKEM`Y&I;6t@hxnS3muNK@eYPpjk};p44ZCjyo)su%x7K!xNso% z6=P4lk`&BAc>rFn)a?bGL=uG>?eQK;9n1MDCoJjgGR~sfhg9Aiv_*q z8qR-8T@8Qjdk+F^UH&>hFqfUe{iXi*k-kIgZNF2^fQLkv@iyo_1R5?eKt;-w7LcXP zQL=ts_$^!*@dYtP$S!3wWSZQ}|Msn0njhBz6OBJ{02B?wXZ_MWxexypd-$c65KyP4 zc#hBQGHlo|ctY2hO$-FwM1WQ=w48M!d`BAyUwUQNYh9@oRU3v+H^6qL^}zYn8If*g zY=qBY*B(7;QN>m2%P|Z}>>x6&d-jMMjint_h-^I6=|K?5ov_qnvX)jIQGTO5+PT=W zcEg6|5*H>-KuiI+d7zwQ#X@ZBM57ZNNSO(!EF#Rr2b*1f6_xj4jIt-#=-2rys(0a{ zfWp3)aoH6GF)aBwI+NfzzOsBVT+$0HPEIxMo*l69G#-9NP3wnyB;v+=h6rVs66^El zrrgWZc?`5>T{h)E0um1AlpnEQSkTl`94G8Ds#9l*Kk!$_)YR1Pv@9(3?~)2Gw20HM z+Wvs2@|w@4wi)4dm@5T+y@)%5h{OU?S>8 zU+@e)MUcu#59`sq?zSZ3k?ImDo8cGEhk40rrQ&-dw%nk zfWh4||`LxQ(%;tH}x?@J-WtecK-_?*4T(#6fqEu(DP(<^J6NyCwI=xoVJ+^)hk zP3Fzead`fx-Plp3zpJ8%5`|W*gpS0?qVC=rrcFliG)x&q*=GH#`E&{yWhWL|TGoar zQXlC8ktLhm-#H>emE(uVq{!kRbxsM}()xs%wRK&3XK|q-!)@W;@jnEI$nZ!BZig_# z^lL)b69DpCMGN+o7_%t{rYuzY)P#~9Y%ZUj%c++2|1+w-fH7C*v&_1 zLK)cHnerS3I)ko`B23PaGbI!%zAx^^r=NXk`Gim&jnT_vc!?d`SE0R*x>EPzpn9Bq zZ&hj%#Zkq8dWV~v4^~&d4G{PU_iNdmU@#dP7Pju^iWNkdeaDUkefeQM_FkIG@ZpzR zD)|qeA5*=W0+CpmDB*HE9Zs%G;eVbR@59Zi*^&7NoCBEYgi8p-oQr%&f<@twJ4!9} zl4>Fk7`#1Q;aX~H(Y88BRv{V65VCsKYy8pnK!;+cJa>5(cmcEQmQXD)6cS6zzfgMQ za6vt=nZL427w>n5c1!$|NX#@N18J8&JdbX>p`uKk__~W=p;%Z3UK!rh@<7wRmr)OT z?Iivks_>a2&U*zbkz~|CTzxewUP-zq$mGmwmS&_cvhYF_!Sw z*=k0$lqHBtgkHFSM7etPJQmW&uVe1|f^PB#|6&t+4=BXSuqWdNZ0EZ^M7fcA{o#Yi_rxmV+%>wbBz%K^Hyc*W_;q5pI>d_^=4Jt`s-pl0t4wo=7mPg z9g8%O1jHq_C?cHPo&BNCB9Z+j?)r;NimU0u6%^LaQnNRMsQlfdAcRS~6< z>}GB8f2rWr`BhoeL3rQ~ZuA6Q-NswA2XyLXVsOyzEqgWFINBm-buncV)xaV%iZSCb zxJptFgep<_C2iMVbZEf5hE(`syl}ddeMd1FehHMJ%)=9`&PK`V@u%4j^5V5-t^gQn zhu%}T5whC|XDAsLkEosjCWPK%|Cuvp&Du$b@e4m)Ujg2jD-)Af0`z@dgP>hbwBFcb z?p@vtJqmoJmq1G1l`4-6RlL4`KH?|baq;^Al+rxC&L$IL!+0Bc^S!eDrXbx;gN-uc z7Ke($|6B&|64y;g&U&|+jV&j=`ck~jD`P5a$#tj}8@6v>{zP|Pi&m|6r=qx#ee^c% zjWW(X>;(JUpLKIZ!DZNtZm;tRsRLa*4Z=+^zY`Et8{Q);5uzp_G z-R3gaG$F+W(0k)i?gSqR%IGbleIhY**^tH_NYlxz%=Nq-`7KnmeF> zob>Uj4!FK!@7|Wr&X2L5c(14xLYNjE>ea759tbrj8YfC|q~I31GFr&ysq5Ld?|4JQ zmPCjb5Y$qi@>2_C@3mVBlUr}NZI)=Lv#In5h36+38P}2FWuEGvxGM4t6l?_?+;OdJ zOGUUDfF!nrfR}$;CgrLQEd(EdH@wTVmZ#`^fPbLl` zf!%4~z%NHTEu>WyaASJTcGd z$7!GVn))$dx!Kf@-u5YW^Kd9}%W-6@a5Se2h$Qn#!E=87;lruRkGx?X8_}{r zG627GW@vm3w|jE7NY1{Bp8|G)PF_QSC3>wF&?q=QgA^dfa2%NCL6av<8qEYX5sLxw zAEv+HS-Qbh3}`S@BbeUj$VkU1-&?7}Xj%Fiht~$_YZ)v)0+@P)LUd37I04ohefr}f z-X(XrKTq<(RN}BH(Ltys;*>EH62$Utwxz!{dK!S}woXNInLn=MCS)r!}T z`G{ZElpgc)>dcC{0#z_8%(BOE`d#b)x%gz@VZUPZCYZ`I3tX;=W*f-UjLFUR`A)zr zBs^R{RJfXyJ%L|mVk-oLr|+71Y-iyvwHU9blr?yIdB59+w+Z9?&(svDg*~PB*~ZcH zcdVd57b)rnQwxFseO?i`y+^H3$gNS0Wp89BBj1@1&+IDA4Wr-c>VXCmk3Vw5T@ajM zC!f+JT#d2Y%D{O#(w)*H>E&p9=>jXv(VK@G?gz|aK|x6+ZX>2GTJ)w~6Z2{cgkiA} zhx<6)>hf$lDiF->8-jnA`I9cFWjwQ5y?O`!io|YZS1L+wBflLv z_)ZffuZ?-vF5ZWe;5(Yy*qEH^K$VG`Me3vR?r;`2iM&Z!JD!_%LzbS*a4}Y0gb474 z@*lNixidf5j@FH;pfxPg;A0oSUll8+;G-$7cvOGn;5r$9o`S3F(yF> z5GvU-co;?2E$Cn&Zl;U4E6b3;uU@NGTv}SIVZ(AO3N08rN*^yR@0MoPCs6ow!utW! zr&xsL0ku>=Sz%zp?5P2z$aqeMs&bVbT-CE2{lL0>aZRZ%FZf(BvuAz<61F13>kMd% zm<)estf@PFx|d56t(`z|c!9?u-$$u0X5myuqTNR`-DSv-)^)dy4Nstq4s?7R#qr1S z8v>RK$!El4hQ$go#o}Sz);^AVHwW;p*O6$(${?0{O#`X=f!fYTOZ?9E&6^K_2 z!@R8k%sFW}R!jz3{`@h`&9u1!dWQ2Egd#@SRKymg1ZK7>P-Bh!Fl<(wooi9}iN8X` z*m-1Jte^u-bwKO76*_Qk$H(bleX`%8ngS*_sH6SKxTsPp7U@)GpV^QEH^=CEGY#Pw z)M8e!3#o&hPSc>DZQ-AqfqL`Ft5GuR+Z{*!UIG@YXJP^3+HnJz)xM)g{lhm%X+QFD z1-r{zpc2f7*hN6BMV@y?WE2IAS4WM}FqfP_ep$L~ng6QNrS^={_DY@|IVL(YbX(JTdi#OxR2pe^J(OD5+Su){zdHSBu!Sx}lez07Hh;l6 zUji7?qPjJA|8pa)NH(`$JLLB5+o$NbHf!+gZKXuedS~zsflluwOXncWNf`7gYD3th zQQB+@G-8A4$;pBeKDof?5f*>Rs{4*zyEdOY_hoPzvImm0W7P{rCW*T8(4k(GIm#+3 zH+Uy>nVVj|oPRfrw=!1gfWOM9>5lqd3dXim{qqLyIOq@&QRiy$UstE^(LaC+G41u! zm<=7N4M9A_#=$z=eoA|Ubgx0)*yrJ~8yJn5^dAZsvCvaAsu`!{qAyYO!^?x#9P7O%!q=EsVtp0e@((za~4u)Sb zmerAdQzk@6CB!)lRC`c!#M4~^t&+N-Y&t&N2t~91 zw;$~Q7Cgy#%U(k>dZDfD9vQ#|Zy*H9%?d6wa>f^ckBP%V>O(Y;m$}4kOpxfyCPLKN z+yCvN4j-91rxDYP@o4N7TIb~DrTIA%D_5@kz98<~^+%5yi)wC;<`v2ys=lk57TBpH z)tv12|CV%)9`XI5pd_tkL!NHZx^?ZNQ+!ipRTZS$e_7AH*7D-=m;#mUNOkw{5IP<% zC3<;548lcd7>#D5Jg5IWBESCV@FVB%n%0r>9#HSLILx@Yb${Q1S`7cJ#w=ZSjo)Tnn_O{3o8UG(cjU|h<#~easO6l|qZKfw zwK4nW6L`BX5VSd``#{?#%Cn;#TEMqyFLqOqIxVb$pdLY7mKn&A5o-rw9MoZ+2eJ3w z{rhf35WS+ae{kLjVl9IF-}{EpL+21Nq8^MTrf8tIvUZ62fBB)nn!z;Z8rB`>>tRGX3Gue^2 zOiO=~tm7(VA7hzPR}KcGirkTNsq>7U z&38q0*t}!zlwC=+db(zrq%CON>-6I%O`>#$rxdofS0av+1;-MF0Xjth9y)A8MlpVQ ziIYaJTBXKwYDO>-;P05kk8U8Eg9kTb>_EmGFQHTxagI$a7~!w0&`<45a$)Q7+R+&q z)w9-FzRPO#>(dU{MSYMBf#w={w=qV@=g$w)DyBzcv{2*u^XHVHA6B$lx&pt;2H|ze z^ad-?v-w$`?ANtxH8nLg>-b3lN7d{U87qv>qZaRU_;9+3iw2>$B4kg!j~MoSrc(2BOqI5rxxQ8ew7DA`{p69mZ_q*; z{+O`k9@^irsi~1I!EFEj3K+VW0aWn5Hb+Lsl;+uM)NIl#k>V7Ze*j^i1!JLDhFXCZ zB+0ZjCjXrI>5eNvr`Ka&{hf!QyYmyzgUBpX#e$odK?uP$Ro!>W(am%-Vku&LEc;=*i-p6!5u)QS^Lht{S@nM@9|*AAa*J=)c07L0~_n<5z|{3IM0yZ`zjl@C-!PI4t6@Xtd$e!TlC zKr>c~Y*Zg;>K_KLr?MNL2U%f@Om&8B8|$_Fq|PolPF+|v3E#U(EuQ^_PAE?KC23Gb zkW-VBetWqJXFMo=|H|36pWF^~jaje%*@>N#dV};;uTcYMfH!ZD2Ks~QxMh&|Cgen3 z=m9ITDF`gZbK{_TjT*K3xCoaOBO{}`76*oG8@q`L@C97wefr1`@8l0wYkTWCL&5M_ z8acav$Tq8gez3}xs$=l?<#zCbCsa*{qwhP=AOhUi;sHtF6}x2Ax2xx|!qzJza6?7;qsOl@TdCO53(dqO z%+}6sf=zLLmMes{wCnmly(MYGF#GrBc1h}4?xOt9iIv|(D>FtqXP;+yw~(RMHFT|a zL5{);$S1@7!GkGWEi)RAi;ks{zM9+*h$kZi(MaSAO4Um^i zfAp5EmapmE&Jv*km%Cp5`E_z)20Cxa*MGZJjVk-Z-~M92R)#+`C=-`Q$Ha^RqgMdT zj2%Y9w;m!h2+c5LHBAJb;TEa3Zw2ThdhC)*DWH+k?%bzOy_z*^{&oKi{{tE74tPxd zbv-S2vZoKFx{>O zK~dmr>>x4I1|`_d;|de22F?t|N5{xv(%PifnMnr8?#Mk0=k42H#-K`lgNAG5yBlbr434tbH57i1NI3+hY015zWx^-ZQ-zEZynG>|$ z<}F&pVuZv@oDa5i<;tzNj|$xYJR~@w5N5X->wuunqYDqs9rqbf%|r76Q@sV2BbR_;mXQDk4|PFBt{sTRz|@dFwR1h7LMsl$gSN0HuYFNrQ}N$pZ&;>6pE37~N8#bXY5 zWDMImuS=;!#5I{5*b=U-Ye#s^W1#7G*f3jPzgxzA8g@Dd?T`(y1dpgMnd^3&I28)f zKK3d^-U;M?fcSvu4kTypSK<%PmcN>l(;&2&>5$HLQX~7XT+GEHOKpdojVpNF)JUoY zp!^%0`hom(&RlOm|D?zR%Qbv&V@at^&JpqEHSAj_VkmmII6zJ* zk#KX{k{h0*&^|7A1m~I$Q?GaQb9xzN&`?2u1=leZz2aq`v&SejiZPncT1fRvv-__p zB1@iF`xM-Ok7D3(0y9_4zGl5SM+gVc=n8g#L6qRDP}|JFz)*tV>>Bzn;SrPBmZx}q z$n@mB{USQHCO9inR5YLm75HxD3BXD?hpsf-FI?20V`Q<-r<6)&JAK6~ng4On4csH7 z_eBs!d=LoNY1*CsIW*xDegFD;VbyP;g5MS%BVkvTG|-qUvG!)Iby39_2E%y zfPbe|84uTG*XdMIn`t;HUgTX4b{#%!Sl#XZn6Px%aF)0yP>GoKpj}{{hcM%bAH@fF zOtaGhVOj03_RN(MvNlBO=Jj7`Q3~&+i!;Rmx{V!DGgkx!errA zbX6ktTRfulg4_Sz_=Kb6Bx0Oaz#Ie}U9Q_+i~DFw`8O8Wk0D%jn-+Cz(2Y0)>ZA;z zT7o(v#z9LVBq$aK^x?g&VPEhIn0;DLX?v9mVEiv&ZRHM&O7EZ3q{K4*;ROSh9ci$p z^>Qk(ghrRmrsRYVpgDyg+QYtiS zfx^tFmA%MAsx8f{om6@1QwOH=EwuOpq9XxeU8?W#;i`_LTpP%_P~3*48PhE!j7B0W z8*(l`FjB_oY;j&&mmhs5CI7+^;_?+1>00CJ>Jn$GAjA_wWS{5`ty`~ zBS1zZ%`*{1izu}-6_$2wUXx2V21kUGu;UyOXf6U9d>UZ~B`{$^ZOz`jOdir}1vGHn zzHM8b?PH)l)B=boC;=a3}w%8p?zEPKftW9UAs&P772FEL-B7yb4X@Xlv( z^uDMD+Ra#G+Qq{=DS#U^K%z94<0Om)2+22?T)E(08Cyy^`228qel~e#|K^VNcBCQj z;y}}Q0K!l-B`}c*y!Q$o&UYqaUyu=~v)Ih!>iVHVUgZfhT&F=v-f{isO|Opp1Q}<&b_0%zn zdYH}z9Y(Rsn)6g|^BOK`Z^szn;j|P-KA>_C0739*tlb<&1T!)R!gO0%#85r^&6^rv zffP2vdjw;d#4Hiv?WCng#0sKIKv4c|$9L5CRFlWa8)jvb{=2f(iPHw(`#g%cdp->S3VO3^m??CO;($3r7D zg=<4iAtV?|*Pi|Qxvi`K3)lf0JXVohAk~lH=$Pm7luXyt+e~~E-kUI9X3l(4@0`|w zs(2dki;Z0MN5H*xa8erwp92yFu!$J;phBH}8M@utsjb_zk@w%4;4?T)ik3zrzbh~) z+N{A0auXoHp4J1sHNRP#z>FFB<8`xGcb2_t!L9LAJ7$3uI1?Sl5epb2Yp=_3J50W|Wjr&-yYZU9}NP9mC$SI4v*w7KXZA>?s=eTXj z)gPLlA27_nmYHcnmC-f~3)_LF*A#vtF}1l2lB<5*@S{hLoFqbH6FWoEvRa456%NCu zH1E)%|B!kL)ZjZ(e?lAuS8W{p7{6uK%Dyl=@#c9pI4g#nWY5;B)(3s^9qr*!^;UfN z9+UyCnEDi$u~G14X@g$C|29+{Im3C1(l|giOlzlqEaY98LTI;qeq|^%-Zn_8)_DTp zKgrCrS?CLeBQbatjIbL(C;A^%fu=BNuEBtULRK&1m9zwyg%hqijAGm!d3($)Xhs&- zmrrZU{L87LZ1w516V8u92gw+PH{30yq_lJa;?OBh(H%5ai($ZQ1eih-7!6hr$$S6) z1sC))9DCz$m~))uB-7-6|MBj`Ik-NG&NHeIVn3%x{&|1=dl2Mz*17Ozyj6w9xlOA> z6i#HTK0WPaT+Hn-YHMDt!}gtUYX(^5J%WL+?|ClL?&-zpO9PL>9+av@QE@G~2Rx)+ ztneK4OTRxuOmy-M9j zXweXUF68+4$~bjZHn_9R?@ts+inrgVL4}6Hr(3sgzuHhy+7SY5U~r$lef1TOtiSR6 z`CR1(MRZ>csbD@cEn|}N2UP85(au%3D0kGq$YkZ;)CiFSmNe*2u6wDlUvcvSsr7RYMeb!7SQ<@SiQ;r6*(QLwgJc!&wlPmb?qRfq}ulLHz5z!yGa8jrSd_XNXC zU{@I&NnJi2cJ22qhaHovdCfr1fURis zGi5?0VwUo@W>}9n4Qa|Ie|q=gMdzryZ@Ih1S$e0>a%p#@u4lvTJFQ2z!UN~~A(D9N z-fieWO2Y;5w@4+qXEzxNfQ7 zJz`g<;8P>R$M#@g|AXJdsj##%J{X}-Hg$il@T6#Q+d11Z+XgXS5EgwE}@BcSi@<>y`lxMC0{Kbm2k zs@gw{t(5=AafSzR>(;F`km*gIL6Wpt-jWgpjw*Kr|)~cjy9|b{B|Z}w>P!d(9i(YY!AWW zx=@y7fpyVyD(l`2!g~#7is`cbqtBI3C!T*WfEhZgu>T{&G@qa8oCUAM!HWj95e6j? zf!imxGRA0Y*Dd<=tn~XifP?vTv#nXY)E=|6ss;|o`kUWd^%sm5-Oq=GHIv1gR?lAg z*5N1k1qC$^N=!+az^*~J;_%4GmcdOjx6U~1`TUS!TWaEf^?kc9d~(%sot?{!K42k@ zfE>6UneOFf#q(mL&ZzrwP3v8K2&GxDHzANigbKaj>N?FW^w=Q1KkXtTBfVAK8QlzI zoldcs&HITkIAmjtEF@mDJ$v;^ytdVVW$wm2;`{TkOB=`l$K|1J$GOG&t2Y#(3(^+O zNrgrxG)mqckS90iii)z z0z-A91&*7XvqIRCo^-Y&KDR6+olb0%8<*$lQJ8Ar z;rS*-Z6XV8E=3w^zjhPxrbJH#Wd@B$JUk``G^kzs(f1$oGyn8omHg&}BfBQCBJOkE z|KmRLX*LZ~u-nFARe8qB^=Z=3Ym6iLFkF~dF!F058vjk3gyPFz`|EwFVg<1t&i>Zdexe(gFZtJ1l?G+RI^aZq_)sEkIGtb_ZA{`P?@nkCBMCSqprE;xzjXPsn;f#aHm#FX5jUm_uF7CFA#e5kV#ZMf&#>HJ{jHrc@Xhoob& z(+kj3>tfPt4+4)VjFMQbe~SLdd!w2uWC?$xeeU*O<< z*dc>zz2bA;B6SO?3{sf(ryebb=}n|Us1ft_(4j*Tsc$9J@QMCJ<1sJzSn%Ffs*jZ3 zWKCqq>%UEFBLKHRjkOy!!q>h(m);pZ(t5h6&qYOsy^c{n?`6=_Z|Xn$x*5NAN2?w; zCS#59Ip~&x1d3#NK_Bi}fQ$Xay#Yqo7yzxLC|SZN%DB8b#Ah%{hJvmq4(nx+>IPCj zhN3g{Er|0)qL@oU?|%IzXlvga?0WyraxJeIw8{MJKhYRS&r63KU_VO1rAC!$MN zwKB{NjG#PsQrt?75eL1_y7}Uv5KN$A9$eg7RDCU7>7O;4A+g-5l{5Msvc`~tlFK!E z@bGE&ndQk|_gaRZ*}3h72+gfh2!IgP`&nLg_C`8^-KogqHKoT0D6-dM8w;S^o5;lC zfWFo99+Eq;gc^gsiM9|gUa}Q)i}O3)#vpy_>0KbCohW=VHjs;b-3I?rxmS}FLzI!w zgN&?h=`ndh-rsjIwh6yQ+zd}3Jaw~cv;H}jS(Sm=UW!j`ud9sH*Z+8PSaul1NU(RB z0R7bH!hImk?|VAK$fy;bvBJbee`G>lTdvC`^{Qi=GT^_xNj?0jg}V7DabHo)7;Haf zV^d}p;5#Wb_wmeACn^DwoP_-ek9c2lCiesZ!zN<5Do)V4hK9c3O1*m3VItC0V+owD zM=2?8DkgJ|LY%kM0d9jQ=giY|*Ae(#CQHzTMFMB({Y>E0dXin8032hv)Dsf=_w0F% z)nnPkP`OjIgC#@D7+6kT7cb5XpSlOiB^;#WD5JiQ%uP)BJ^*8)l zoUHmY%)C8vP#LlGezpRc1X)-h=`btWF5}Z<#{mDT(NIk}HlztbZMxM8SEzN82os|~ z;J9Ou>AbZoq)BE>2<|gpQ**ni@B(FKzTYCS4|0f`oSmI5=TKVluxGz|wS~9X8C0Cn zwrj{cSPjJ*J|(Pl*O{7DHu+e%Lq|jtnFk0SE-mxm>x~h=Vy3 z9C5xSgx3P8w#*3cB`hkdYWog`Cehx@Awx&?6fA(c+x|rQ;X6Q*Y^%$}Q>2JP23p+bb*CX9dCk)SA5Y1(*w2EAl9Y-DcU z0;b+W97|LP2yUra7nfB7CiJz;qA{~s{=nfyV&3=%93E_^YD3o%)Q$XX@TV>bTrgZ# zO^{vMEmc3WP*VzQ zSPzP@CYJH}34xteRa=0s-Q*#7@#f7oI09PF4XKiMAzC7e4RZ2j%kj#U5vL20SKx%y zU@2>^_zR8wd8$LyTMg;XbzNQibogl7|Qa_ zQY58fqbR0N0e{MzkyOA;a1)eQN;a)w?=Yxa*FQ3u(t^>jH%_B>5)xVk1qTJ);9|;P zthXzDn08&)I9f_!&2p-RsXEqde+>z<=>4N zfpj|oO=-8ZROTO;;X=ckke6I1oOcS!X787A~FK;R^HBG7<`ct=BH9B6iZtMQSjRYyHMDsX{UIhi}GtfC?>wC zdVbof=w7>b-9ztTgv#ZU8lv$B`&z5Z>P&D-^7iwmy~v5b3gtkq#rB}H}Ey?&WiOwG0I z@`!Bb^Y!tj%6IrEWWk<=eaYErG*GtZ#h9BWG>MvWacQZAR;Le((Jv0c0K8!oBZapngmLdzlYy}^Q-5lYV?LuNkzB?`x5D+;=>drg{;Z%;O>UvrC&g-G!s zB3ULHWL00fXX&eQ0qKn3^+ZrQI6pkcWJT0NzdKk+Dpcp@7aEp5&yQ0{6+1`Xfq_Sje$R4K zp?jx<%**oD&0d1aOgGeFcIR!#7x^9zd?670;hZZPV2m~ty*QOv?0gu*PC&r)Q`=q6 zL=C4?7b~MaiAq|h5-krXdx%H3-0-lAS5-DteA0xomVpQw@YAQ9>H0Hg`hk|D+g=MD z!XYlXJ*wR7mH)2~Z#r%2-hYH1`77J_$B$=-mM#>SkB=Jkr^!SDN~DEtpCe)!i9!3K zo#V#tO`;XVvosLiVMCA3efv(KrU%15G$-Hjjhhi?{X~VwRk$IBb}J7!HJD_4FQcDb zYC(C|EwLwIdG%6Li@c%1C+$JQd_c9R0cQ1eu>u1Q;hyZ zxhOsVL_dv!N8=nACw^RY;&j`zZvKQOYcb6&EUF)_1+;K+Voe{h z2PQmjw2*HnzM5&5eQi?)@~_Y0?)L8~>`jpd7Jpzs_sQ!IpAM?C zZ&ov>13t)+?pE(6hhJ%42p^7ltNqXK%lgf`Ivaf)eU$Z>?Jr4%Z5&956eTRNw`D;6 z9u|NB%3(-xOes>2P+iJGi!7%!Z`W>iZb%$stOSkXV<<+72CrW6<->5}jX?sFthxZX5YOO;LLp-cCXN`&pCRZldpULM2qFK-4lv1olW8)|bgRdT1G=oi93 zB!{R!1BCGnRPBIS+$YgCBMD7s>V~+sk$=e_m3Jijlc}X}*1mxF_UM8xYay#io!+D3|$MU4HzNzU+mP&jf6wIV`2_F{9(Ax(- z#avlDrDixoGeA4n8$1al^eaL|L<{|B2YbZk{8;lU`F5hhHYGBaU}(i^XUh~uV6)iA zKc8!J1S=1GndV2H1=%dgn^$3Nu*8PXr2`-LW=%~}gC9@V(GQ{|gXv}3hdGdQlf?{) z5<-jeEm44@F>XA z!(yj=o&yntUR)lnUbuQ{iRxCxcI|>_HV`qKn#(|C=eX;lCC0o7jTtY{+CH){@c#XN zr)Qq;pkyHN&KaHuyHl5Yg7L7U$q8tXG^D{#*3m)~;)?5Lc`FN&>Np&s1{7Vw(TKgL zSue{`Y<=7>%lT#bx9e;kQKcL33Xr7Ff+s(1+B5n9jj0R*38~!czqlK|WbpZU++kGB zM3tPs3V=COw|@P;U($XFCU3ml>~CX?$|Kmq7>r6dyq zeS=mKR1*xccJJR?WjGny`1h-+u++$g5HDKuu=)#g633x9D_Z-!Gs6!@3FUcljYjFu zo_&nzgxUEG1ToPv)u=MS$!<8ioyM;>_3QsP4%Iw0ogITm+{tcQJT?y5n`((5x!4!+ zIYj}IP$#nEJYCd?vyw|Ortfn<@mGXVP>6}!UiIqol7GKJ21`oO^m@=JTo_ z8TsmUPSrP7{#`rKEPw*e==kq{v+94$+!XYO04+%7U$f@osfmAo@5Z{@es>^J@YBVw z*GodmuwfaAr~ZDW(c50V{NJB8#fp^|?vCs_H7$lYs2k%{RseS(&&#>P`DaV3-ezFy zJ({Ny6USBkS$;io0J~m*t2{8DVU8u?;yQ~~2}@qR!1MRxe$riz6Gsr@jde!2|MlzF zldr55$K>JP`&7Ns@6}$_-zg|O*>>R6Kf=xb{*RRn>=Wo`efww&w%t$)x;-u%`%VznF*tN(9XSk-#Y$Vmr|$3 zRjTBNhDnUIuDvEi$yLKIS&^_W@`+RWh}eNk_mFUAp~#mN>bk8OhY zVAj|=A1SOQV)l%Kbi{1$%hlfx!?hQcWz&;%kR+B8P0yQCVYv^pw?3LM2+AVd-=jfV zlP2lXl-yg7jkNBU^>D)+sF*+*-sDXG_ww+&Cv~cxn|hLE#X0bSe89Yzt6~-dHx)NuO&0JCP&R4r z`>R3t`xSIsR+W_37E4OT{``O{u#YJB(v~bah;-bj2mRWFimsr5j zQHvC~Sk8R>7))9T1g}CPcXh#N_(F-O_Tv+#umt z-n(-r^!u?`Q(d_LEDIL0(+(f+!d5XM;|jm>s2IruY?}ZSN}n9@Xi4Xqa733m?g&%IlszlB>B7r>u#Q9TbA=J26%Cnd4h>*9IcZoafixk63uVecV=d;w2GDfm&B9{xeuQ{oh`l4LLI7m z(KOW>r08ONb^Rqa*u0tE92wDy8i}@LhSaOpO20oSPbyvMdY=R~a^yYBrHEXTb(T_g z0;%WqQN|e@PqFL3UZ?*T^DXQY<)V(5%FVUML@d*jb1E)AfDxB|n0==o-wM{%{#uVp?%G=Z}NrGjw=gVk3N@cVJOw&D*;Ro?|8{kDt~&4^%n)YB_8X;})%ZKU^{| z0amgKYqUC^BhnXM`&10}IK?SCrqL;Ks}&qWhrHkmIG8Rs2~M=U@?)@XM7|l%&CLj| zlkJAdr93uqwjnRCZrHH0ti(Q=MHBZyi*~`1=r`=!M1qbK4@`3a1OsWF{pZCvtqy|a zGUVe#wRez@Aee z)3e&OW zOMK>4TP&TT*mLcESi#`QfxXCx`8VqxcyWo9b?&2%$UMWI3WB|t2NI&1p2;9;;mMD% z6;lCcb&6<=&-Z-9^Q9z~5K1aDL$Qp(rT@sJSd9DVV-QMtL441q)>2xd=?&>h^etjwp4} zg`@#5+NOq$xP)Y|%v?x5cg-~z1_w8+c}SmRDt5o(QX(~V$sZLJ zR|NqPHl^Ia+;5N|>HZ%x60{p6j$!|N^|dH7Zb4x=P27#Z3RL0RVmPpRIhRtzj=eK@ z82hkADOvS$olnJVN5yYXuatbIg;Q;7UMl8TCXO`~HeS978#{!gl%ev#aBeVC7f@Hj zX2sw_GzEx7>4D)M2pWD|d9?;%>F^c~5ms#vC}-x27ZYHnF6)~*C1sS1?#gCkcV4%R zIm*Cn!`|QPn06bnqq{d{`U`_O(N{7O7bx6Z{;jUt<<<2jx|+3uM61hEIu*;J;>&5l zCTB?CeF(3gRvT3xaOLyn=WE}!pXmCB!p1otpI(~5b7f!9y7(ZgAd{df>C)d%PI*TN zkQisHV(>$dD&E@l#03X;BwFgD8O{-%p;R_tPLvP5xfLJ*ax#4l2G}@F*IWi-p#pIv z{^;N{$+%8FgNXc09@x%=|eJ-h>gbmZl?JdJ5?C=|rd4ADu5=DG!1S*O$ zwgemygF13`A7rh7&(rAtsTogFpl6nq&AqtPYr;2HA8c@Q7K>Yo3&|D4P|=0w-KnfC zqRMmtBK+ijV!>f%dq79J?Cb6AP-QD9*XQJQ@#PymRGside|j`HkiL)oG#;FOKy*)k zUMuD~kP1gyBgfTTe-_p!4^n%;Hs>C??DtXyhF+ErMBi zv3Ke(cbFn9X2BXiC{Guzj9hfDIaSHs*JbpTsq-pIj2_RMG?36)z+)s%EV<7vU*Kas z@Ei)lM(p+FtRD?B;Q<>rc03yl7p^$d7Ya(Gy)m?MJncSfKhQG6*;8_l$s)ZOva+2eYohH(IX(UG1GcsJtn@QJSTS$aZ$`@s zOE3Jo)Q658vt1Rp8SPL|+tU*}tQ}u-%Zt6Lge2`rlO7@66V5uo?(23CEP~S;oiFz# zs4EU#?!)Z7IeH_TH~%TM1XXkj_>=7am?WR+vJ>I<%VlFr3aecu-#_Z=I>MVziXiqh zCS}siK>-bCcbl>^sbRBf3Z`wDq^OpLO(L;R*`Dr%e`tVHS&1Z%3``~g{7wNax7D$Z zMi4H5wnCf-4Bm;pnef@+7*pD%9_T>fh|8(_^_ZZ z!QrotGMwwp>3p~lzst+il7X3o7K{$uIrb?iZyeZgn2z2HN~RUx+}$$`DR72X7w0;> z?=JA!1&tFVJx}IYJlwZ!L#R!wY|VZ>=fAp|gA3S-oDOm1t~=+}nx{j_hH6sP^qPVI z_jFcCG3A8=#H#GI2)oWyc_k(-P?{*$o_%!yElZ~Iq%!Fx(}lS$Zo%x#2rEq6%l5H99pCn3@HJ^C`I+l1%? zZO?ldM)f;`iVLVYcfQcm0M`0pNzyc{YgtLXCL)GUf91!uAYq7zJu6mjOk)_li(MkR z(cl=Tqw?miV3jURNJuH1?tY&D&HR}#&w;|*|8cRn@eMNh_WHoI-rc(0Sm**fVXkAL zL0INi7M&)yuta)z(2v3ASbLK~hCxwp96J|=kRA@M!6M<`ubHk=%D;I+tMVZA7_m6O zlcyU=3s1QnwBN7xs=|hS%_F?OnD<#(3uW9j${;tvO2AUrg_aUnDmHb1wmMos7QmqzJDIC#YFl7`l+C&O% zclX{F7Vn0SEXFTSTH%1iHTtEyMu>16!^*djqkj}oK!-gPEC#Ov%-H=TDVqSl*n;7rfppQ z!PO+;(?Wi*IHIW_Cq*xG&%4oMZ9Twwc3bnm+k(lPC#3mJ9|!CZnbZHNH9J=3HsYep z+j?S#+vtb;N!Mmk?@2Te)r9?IzcXwl`qAOwpe>-MxaJ%>b;v!@GMTUw&X$#U!ArkK zq>5kc(R*^uEwL|8wCdw6_2{sbcWj^0^hK~0uNAXh*n!ebNuS0@R{V_TT*%}giQ}Y_ z4G1b9pwI3S8I|Crm%+U0oGPFhBb!t0?GD z?KK+W+BUI~6iWev#S4NQ3-;|VD_p#8~x!D*4JfGcx$N(w_>~$|a_G6b!Y`J^0+Qc#bOe;1aV5 zw6;kXd_Zn1y&jndN^M#v?AaEl(l$}y;!ndADVOY~&lwVj@3vEnuE6jJQzctR1f1zU z<~$K1(BaV53l~-&&HBA!Q^*rs?qoa3?#!p=1+eVhMm$%_@%|A5rG* zssH9^qGkAf4T6;Ml{M#d)SJu$odC^9B1+k%esirHMBJjz5AZxx{chh&7s9KspI_(e zsiXW^_aUhx-uq+iLm6!0iGc#6i)UZ9#8Nxa(ko})J~Y^%8^>zRl0Ot$#B!KbSwr_I z8Uao~U|qDTZ9s5~=FR<30TPCr!8>MB(i3QW&)y=g=Q?Uz+>B^nE@aN3GEQX*NY&NP zqX>{AfZS>E*fR#p=l$u5Hd_uIN?x~S&CHarZcIAw|NP(RSP68znN9Um$gYM=K+H8< zG*5A`e2Ih4iuz(SMm{lLhjT!>Z3mN!MrvL&zy48r`D1hqg_C_5h>O)!2|I;_kH$4h z4_d-7<@tsRM~}j=8$cq`)`r<5Ctbp($5|dMD@HGZA<)C0;kH@v^hyQT-ZxqukQ^gm z;Dq<&V+T4KyQt0|n3VTD5OQ;W#aY}`7Z^;NoEvBmXl^j=_hPaNTF8&qMSl^h| zldtbD{=jzdsw36uswNX@Qc%CvB0yY)A?8Dk4&!s2m>e>-GW)XKpTbQv3iRq?8QMyHo_U-Kw&9{%D*u3~Lf?VUFu^K!wFu(U?Mnii$#CBoAWNt%evn-)H zPUfMKc;mNUfc4*^Lx;0MP@)}{*dcul9SKcQ*y`3UnzRqkz8vkIxi~oer~AQ!3nP^v zLMeOHVd`owMNR~i#X-m8ELE%tlHf>6d88tLg)f>N74ozCjWGE!v8f^Om$O-PT*cWdikFG!hp!(z8%5 z1l|x7<7F5OS|K^yb*G?f*|t*%Z9)2QQAXrQ?|ysk^}B;I!Ii2no{gyR1-BdS{?yl% zk{$;xI#aNbgWPAQZxK9*(kBV+4Vr}Q^v3s(F{Sb68Y?ml2Q&^ImiOk(qDWublJ4!} zePp_fIPXH}tm_I$wMT5}dy z3+PCDCx|#~mZu-onodUP_8uHJj6Id|m}mvVFr`|WDvj!CZMTEOKeRsE4zf+#-0S0-UlFJaFw)0H)F;ORTg^%0M8A%tw(q#@2i)NS@u4yk@^y@1K-&*Y^`=6 zuPG<)>i3uR!YCy^rHm^7(rEaU8Vc8*d9jv6N?SQ?ysAk(?Rvoc}WZ z0tU5bCCnA(XJx(i&C2pQbH?N8#q~YJ)J(eeq$a-~Yu$!JDkq}Zt-)<%wjs8z;uuKL zA|A~J7h=kEKzv(nOBX&Et`x&Zw#5XMz;8Fg{x0$0L1Nb@$sG6BFJ8P546UaT?U6U_ z`!m~TF!WxNj>6VazW_QO(JNfKV#N#^Ef=Mz;x$yF1<`|VnkxHIGN`%ViNu%oEKk;0 z5=rlGmj$dAv&JcEelD;Sq!ErU{T+zPelAHv;ov03B9NX<&|hrT%8~O%k`8;Ne{&td z&H$uDulSP5*o7=~$CR_jc__*?)-*Jhf$GytB*(PAm`aH=d1WNQS4cr9<_u{&&A>qlaBc{EzeedF z9}?s;1XP(V|IxVry!w12~~mHONmmNX6tO~}xwk-yB1g0UX!ZQ)-tJW~fB ze7EHU&NcS;#{Ss?NL+ong?EC81Tg-q)=zH{x*^EDRk$Wy7L4Xo>_Euj)O7i11AC*(EfU-ot&q(DM4kR)0PMYDqNzd3d`FN za|AYyFw)mh_hmToDI*HYK(!ogTIhzBw9w>6>TrPrR;6#9uT>RbT}d$xhN%0?3|vOp z=;j@@00$nUn*hw}oqAW(?+`UWXdMJB6CjwTn%dFI9F&2L!PC+7>LgSbq@0iYf^k0+ zVfI#JXS9OIJU_tJ5cy23IUz?tmd?rOL+}dL8C5HLkV`#*lMUhe%3&R;??@0zZ-=byiym>UBc5h8O+=qOA zM0j`~cs;YBe_mXH%R=jj1a#$Yd_(aZmJfsH7X_TB+CGI)rE`Dh)End9~quSi>MhT4dcc;5*^7Ojiv~&c|8znqXZW;&7hF`1Mc}2 z9V|?Rs3sjK(xgBWpu*LtYJ}A~g77Kh%|2xsQ9p7T#c1g_wPD-J2Y?<$6ajjh{zUc7 z0k`=2^%_Rd{x54Qx?1mD^;!=+O_{F<@PMEzF&B?NUR;n3vaFr?e((R_i40O;!Q zQVLEGI1hr&TedJ9qr6di5vh0A{uUZ<_UdRrCB)YB!h}`dbud@1Ld*;5;CSjFp12>X zVx8?}Kt*U$E(!y5;K>V&e394?=!yLP7QjsLBOaK+(uchDa(4lW&j|i4+7XG!8}c2? z)Dp0%)hI^}6e~cV!X5c&>qM^?0gX+`6$I>Wy!o%9Gi@6wFqFwS$7vpK=NUnj#d{b8k%L^a|ClDWYtLWxH%o|JC={lVB+Mb}1i70_G( z+{f`BxzT;oLDh5arAKJs32h(A_ps}~{Nf9pygwgbB7cY`>)4!!5LE(=dN(kDmQo?4 z2wa?$o&}5sO%$HZu|}|2+R}2wz4pS_Hz&0K?(Xr>9ceYr<9uCeECjP^#$Ttuu+k3r z+|mPF@pTev0l5~JRSN@njFGrutA^&ynWKsBK8RXOp5#JLKu7FfoDX73?izGq19s9p z@YA!@uhYj6aA*_8hbS``^`p_1H}McKHQE3W(U~jqsSNLC2*1!CDWB3OyqHIjvcbow z!*3v>cy!k17xlG&|B=i4NfOS0tM=P(_&G#FB(Vd%4t_s#q?x35@G<>hcN)ifWiehv z4cbVn`umQ_2}mNq9d!LSK@s-rbHDIU<$RNSILh$oF4%qsL+9bqlQmJ>a@hO#&*$B* zk*#rle)J|EfuH-c{?CfHm|H%*0 zALP!!naYLQI40ik{96G(_&{!?KS z`x>VIL9RFXC8e_e$8Y}>Rh#^U?q5Hy$mE~@{qKFDnIE57nVC)Szw&q(Oj=RTL;_u) zRI3HtfcCQds7@eB1EL$s<#rhpS4<<%AUOZ1N8b70dXpjOV&9nwcAg@igE zzFQlfR`>LlPpn$g}t@A4k`T)NXk~>B`M#0WXjY&kPX!;axR999G2wtV|p0Btr^~ZDN z*4Hhs3_oqyU@iN{u$bT?{b4crIX>Is1q!%!DJNV|iIW;T$?O^_xo5rVnZx1a+Tnf| ztqzy5GUpv{{Y%;o@-97F*7fSonswrh><}W}3_^XIa^aFf$U!z~8T)wY+&TJFsfhk@ zD!56(0G}dYj|uF5$h6Q!Y_%SfP0cp&PTslPK)Z=F{XtF(Lv_~7p0|P<8auPhXnFa< zx9)?v+1;sSYXg*yNq;-}8}|>YUYmPUj7K}s&$+fGu!mdiTEvI9OB26ZaiIq+s%GU6 zAqJ^WgX*nv+Aj^YZ}AAFzy0^4FLP#FRE>p!7MHbe&gMqpsGZ)aB4@TSJT0pchPPeP zx4PAL7rBN`hMAVHaj2O2K?D0r1=EU-moMcZB{Mq9>*xiAEmf*7gv=wka?j*9GxRs% zdl;Tuf8bhBa5a4geRp$XM-gje=`m?VMFX3n#iox5ZiA?086?ma(9jFut7v8wy52uP z(X8Hi9&sVmT(xcNfa2RG3xfA*U6gpJ*aRr z0%#i+TyNG8aq3y9Elpda_C4R0S+r>4j8S)g%dOs8q7mjY*xfuw>V^TuPga*=RBwq^Md*~Uh#{`g{phA z0W4q`Fk2sBbz(bkhZv-&3kXLMg<6J~I^qXtSb->EzRwvB)hN~Hf%aZmN(0r=xFg#{ z6Pg6p3M4^A>IpEJA=Db`#f~B7T?uR=xk_*HN}h?qJ9!uuB~a}z#oJbuvP0Ih8vx+i zwT9>kXoGJlMy%b2qO`_VHwfz35B^xJw$LiK=aS*#8U0L2}jceKfbJ-0jsVWDB zYdes|TpGBCci{7#Q1EgYjFKsPK!oX37CKZT#jvbvtJOD$$rTv5K*Cs z-89i;>MhhA#QNQLM5kG27CI{PN9cn5#Of7mX&+iF-fXXd5!*XDN1<0U1OdaUGx9HT zs67#u00vrvoB7rJHtZSHVua6A<>!& zsPAnBuD@5;c;p6B*&_TN4;bd;B74mu^qf{_qLF|whW7;3xO>a51!jFMMic<94Pe{2 z3|y`RXW^>tjqGfRS<2%hva^+bSSVgJwHg+x*ymAx(yYRTZJ=pyzxx18xd@8WL%(_7 zOrnhhAR>@S1sAl<(+6B3oijw!3S|>I)i9!};%gb2^|U0+mmMarc7Bjw8(_O$yw<~I z>LNqyBjZ2!loE=VZy2ZN0fwLiZ_;eTf~QCk18X*0dZ09+s=*q|Envcw>bjji@VVz3 zbX`@E3}ikw6}tw_pnwTVQbT`PH?>BM^I*238p!NqqoGC`Z?ql~ysSeJu0U56fi_fy zVzY|?G4+WN#lbiS*pok^6ImVgtF2o0S=7cVfpp~BSeCWeA%V89@IK4rxF)aQX}Q}y z6KP9*EgbC7i}@bhXII!NQH=sxmV8eg1oH6REMPj+#V9SvwF&KgJSyR>hWMyP5u*eCezCcu=Kjk>ELPwIzIrJK^GLI;CG+NY4vs2i?){| znrQ_ix8FQd3V6;!1S|(Y7r!XSdosd29~SCX(yK<2byC&>viKfXzS(a(RWdGjI_28= zyi!~78wTy?8EU!++Gu80Y&hCb?nyKe09)JD;f3uM=g`(8{y0)rb^uDrI*29FI_-A+ zG-{qHMSf3(jNzP>+a=nY&~;zr!ulNWV?_&guULT2$v;))pl?X_d!}?7_D52Hoh)J$ z>K0DNQki7IOTj?-fikOonDYoQOcA!yC0i?$QR%yllLZ4T6__N`c3w0-Xf7k(2>L$( z6Nq|KxMf`I-N4N0yA=RsqJz{H8*(`Q;3e?capAhD$CS~Ec*F7f9cwY}aeMFGt|ZL0 z&_>guC_U(a|NUet(i7-a4L=`hv@VGFW!>UU0s`Jx5T4v4Pey1qx2wG7G**Aao)r#*C%qAb) z!MJ(Oeqz}Uxa!w<8j4FWC;cUbALN>u@E$aXbF|w;)(1S#j zwwJ?ji(EKXhN2+ybi(ND7L=xa*!g%kx+II)_Tryg@BB4Sbwn|{%}+W>k-4{4D7Tvn zv6l|MT68f}5Z30f(~C3Q5+|S6R-oWyCArnRgSsXWT1JweMTww_mlqV(d5^KKBLXJ0 zH{s$P5|0C6@X+r9uLmYzAodtsPWlG z?_;@SRw|6iC0i=UYxzJT6gvy5a2%$CP6n>Wf}y6B)$FKWs;?y4et*{d^Z4CNb#n~r z;HFcDSH5vND{?5UVLjTyIGRuk95P50wfT?aE?R>X_t)4b`NLJWcaMV7C_-s+d;b`F z0)BsmL-hfK5$A)_3P#(vwBeo;1MCu7FB|zO>ngcHpl*m-xFa4ird2U9@8hnzm|-S& zlp>jy?yMGfn@WYH$EAyFg>qhAf7{EJ(TAKxh5J7n^AGw558Uk977=Uwtkin%6`zk4w4=fz3Q?@ zkGw(DRG(19hV4qq71}4+9<(v>G5#77P+4>c(KK>K_jX>`YaKoD#wiA>T8n*($j#Z3 zwt1P-yNUD5#Wwb0GX>efTCuGfM0E+LjgMRXK2quLz9{RNi#hJ*8|rZ2Y*ZdO-iLk* zmqG2UnJ z?3q!qXC0SKjjlR$rWm=LdArbC zp57MpkBQ%!Kn1v%awW^cS)OK{Fjq~$>d_MJ19?|;!dypT3B{!({~mzk3vna!#0|*$ zxwku=BsZ{jE%Y}FsfNAHXUjb=@HWK=L#|_B8tC%n(!JS6_GrmWvXRR3lh4Emy#X&D zqX|I1Ch-__8;F7tu^%yj$r0#q1XD=X(zkheNU!fna7jKCAVG+#!`D$0vugaGvo>%i zCAiGS@X+nfwpB7X`%AD_#CYd2%=Q3?HAGLo3di%C&A=&8j8H#a{&GPprylx0i?Jlk zlfc7mxyYmv6~Nc}36gX^0L=R%GPF;^)}~C1+$vVk-um}OY@{|cp7Dj!_vynNu|zzi z`@6tMP3O}H1Hx^Jmx*1TvT0D`nNw!=Erj1ClxL%1ph0Op0#+Gxq%_}LM&Sp4guOb- zK7NQm&~*;1N-0Rb7j;5 zdp@-b+~9dWKZ&*ArMN-8okos#Eopl!@wgiTiTGaO`|B22`+aj9mW7i-s&69Hiqydw?ULfHCQo=S2;JgU7)P_@N_vXP{| zhBT&B!V)fyTXOJ5K2~jWs@Xxi2|3BrhBmTQ*q9l{pIiR9SG;)E z%#^{3kh#KKqDG6|*S^Kn-K&n*df*AXA+M~pT0MOuQTx&nND3L(_@GCHwdXAkY84N=+q%}0~K3|SO(@xQBW`awZ14klMh0& zl9ZB_JSf(@Rb_by77saEfc*^^6n!^TM2zf)mMFI3_U(z0pZgK*Z-uSUPB2-rXp!cS z6}CqTsv5P2c5F#@i9G{3evDwc0}_yV=m2CEF{?4KDcq|g$s)XmTW;>%H}MInKq~(I zMD2T|9P{CrB;(Fwsh;#Ft=LtnQV1a5%0nkv(0?0SgyIi~6I1@0Ctc_#-K*mx@aPXf zSR08|erJ3SjZI2Wogo@D1z;f%dX@v1S+@s06|Y0YTkrSNW~vfXzf}JQEyCi$ZB_pG zWE1_w-b5FivzlyGOGA-22$SUI7lDTvZzMCl-- z9ME_J2QQ`;LdZ(>0+gS!&dQFiX>U^Ik61)%YdN5wlb@)h1CWv400Qtc6)EY+1HPtgf~05_dHg@P0{k$~W;LLw5Zm6tf{D}B3EEp` zae63gb)1a4Ujb|sL5FkIem(m3T(jJihFFTSYYk@e$-m|zx}Ex5_>vvgO#K;ffAe!N z;88UcB?_bObByTz?JRi$43?=B))*pcW3{cCkmxK2HrEQ*TBgQfJ612G*hMeRZTKCT zj*Cp94tAN=T_Mb|eXy7k%pg$Tlb?q{O%#K0e4?!W$KhtDs7KBFkbMmx>lxl%H zuG)0)wvwbalnI>X@cu+B%;B9c5uq?0zldLrz7}wCmpJ$ahTNBI2$JKqAI4VNHPW~ZG%%LK_I5hw#Cec}CIW<{gUL3uFoJivB} zg7f;SRH?;sA^Im!j7zr+0rWX?*!0sV+*vz5;u_Lju?^x?3`rDsy$2QVhE1DBK9p-> zW=>?ED<7@fR@2n__s7gHCaTtA;~7Czg%&pmyZeK1QAxB$KDGiq#8|(r z8eQ!t(duO#oPwBnF&McczOV9!Uxl^hy=RWpq(Na`ydNQSOqW&mi$Ai_0-#`ADnO|a zQ8F(Y9(YqojF$BG+)vdX4=J*Ce;;O@h)HB!Z5LIAlwH`UqwI%D|BY@Fw5PC2;r9MK z)_KB0+0TFV^3nk+WZ>{r%Lds%oZu5ktYne?twN+?U6pf;ph9f}8-?uix5kXPraj zeDFgaB%m_%IG06^tCY${TBAJKopMAE@kJL5v?E4ca1sHG{8cylwl6&cys!B9(cv`AipQhZ-%5(|m$ zIjH;Qt;d8b<*z=7f3!a2AsL__pl85DQv^aEBYQyEmC#K&VGC?J{VKDYayWz=#T48V z84_D+v0x(q)<6CpqwGWuO)A3DIgff{E<5`$!&G8j$}7h^JjUY`2b-Z)^6k}rf(^K8 z>0zgzi9V_KQ@?iRyQRfHeZ{*7!yQ#(Yf%%oW1mcaHRUubPTv2_G_fA24}xq#dTN!( zrL2~>vFNt3F|m0GVPk$Ek%$`9=*b7kX^1f|!jM}#y}ovK?UpUcv$>`)3Mdwf$OwEz zbHa+tEcA1s`j*G(&Uz!FFJFjBpgOT4jT1<0HNtI61x!QAh4TBVHip!{xdKLYGnLr! z9nZABg6wkijdnqNrOZ^o$~db3;tz0~ZW%ut35LpSAs$k}^3=Ij$3$TJHPkSOg>G)b zjlv?mkQy=_e?LDA%~TtrYaiJ19i2_IZ?ZdYgoy;9q!6?Uy~*n^GMxrwmLRLA)In(- zR|?=Vnzd?#6j-_T5V#kcx}VP$p)Zm_U<$|mB*gtayZTD9ig@8y70ic@b8f2Ty31sZ z?9@kl3^pihyp29#E3yOenU+fJ8jsZh8 z@}Y}p92mWeNhqd12XN)v7ID-N9(NztKd5@}L?hnu`4y5PJ!3fa*P-jIL=Hx2m!N*8 zAi9$NMxaTs=#s^YPmhh!yuDaKAF4q_{=3Po1p>p3Cd%y~+kBwG(;m79^2CNXIixt( zk#@AWWTBpnaNj1!M$5%X``2-?!dgZZIbujpsonTfrV&)*UJz9N*jbK>32uNP<;Jn$ zFgQhk{a50|dW1VRm;$ON6w?Qg1m9KQieP|SERsRW@}p}etG`xuq`smR2W!8}I9X>x z?+cU7Mj(Rhl6ZC5$PgK{FT9o_z&;o+Y1Z@K<`Yh@ku-s~9nd!>GuRYZvjfEkHl48{ z#V{YysQ(SipFk&$XjlMxnL(~&M_^n(d`O1az8&J;BUQt8Oi6xNr6Zko@m**q>Q995 zgF$=^3&P+c9F_EMPMgaE;Ts$6DHKAtT!~)#IE3LEyyadoQs5RDF96GRTD!c0g(m6 z{>Fqt@0usM4yw%v`$!anllMH6b*UL=Ss)F4rb?hwU%s2yL9;5a)2+awOg;EDvfaZ! zhxUXefmnBf7@03XNmBxtas_29lOMG>HWi}g%I2d)i4s7MrgZfYw7lR$C*G@lOA;(7 z>nF@z7{buY&d;6-ni$U=A%}rUQb&JG`n{P8xc8%Mnx_-Hvm&V8H=R%|!&9(e@@f2V=`j zK=iL9C?w40mK@5YyE7ZDSU4c-BYv$ZeNDbiyj?xf{lhjK_BRgDvA2&W(r_!6ZN)t8?zue2Sn~AAPyG8 zL&kUBjJd$HbX}|*xAiHu*T%O6GAKV=qXg$}Aq5unMpDpdeR+LIA3Bd~Ia^Ad$>epcyH+}4y&n+5RhB45 zi-@?dpMHX4J|r9HAWDXgxS4$g<|J+ipnf%fVxmoKJn& z>Apn=-g)a5k6~3d(i9k|VKgG7m%hOdU5e}QB)&b$MFfTCp#jnjOy;<)tIcgBHn6&%C*FSejNAqau6{~h&!BJ6_X6H zR-1_a^_FBf5lxF@)Vy?x5fVKxBgB4V+%kJhe%Q1CqheiWLNj5)y<= zL389b%yeclWwhZ;2D&RrbVi`OzwK*ZUVryckI(A{l zRaYx}NQtb_!CIv`DVX`#oLJm5Z;>u&BozSBibflPo;{k6u{2dzFbdpw4)>3%k!mdg zmvM?1DzYo$4?4mWp1ZMU!Wk!2;agY z*#jZ}5E!~3iCSL4(R8tq3uxC(BcQ}fVG2)dhhS>Kf=V-J?OzjkeLppg*Hn$Jw zOSMx2Dw_t=IIW>^q&|B#JwcUx8z`-&)?C<@xQwqJp>;wb)`bTQ;fd0VhBw~z^)^cr zsT*SlbLNK#%aU~nX^a`49t^%8bV^$i@boCuv8Dcm85gId8Nf2c*%-sIT4eu* z7=J-9C}JO$|7CIO!+OUdpFmo2+DANZEE&&08^CPi#;5jw|MXQ+u=Zi71ds4Y*`LVd z(|rAq&;LA+RrVU#&QxB`D{LahE*5PNXz~*IbpexP0LBk1P6>3^wBmaH`c^qVVEyCM z$B}lLrlUBsbI)B@QaF&@Ze#ny--*5K#glIk6fAdKG<_oKtk`<0+uN){5O-T{AE+}D zwte|9{ndPl=bqS1+2G%! z`1Tme|JL`c2z7+bErwmX?lCc*k?H~4Vt)Jx$A$HVfp1%sKx~#toaYMXmjO?OnCU2f zNy;B+a6)>@mP=GOK+HhZG1pOP|Fl%@G0@@Dt(|d^emH>ANl1JpPzD(dPNxziV7stDD)Kky!g`r@)LCXVw?Y+m4j0%Bo0Bk77QdLZTx@iK zWQwTji$+b~(}rZ2F*dyh+1+vy{9Fr~ev&(u5C2X5Gxv9C2N%kYo9xUqk~`DT-?c1v z{%c4@m!%<$Cu#xRy!mieBQDE&xKlfEirF3;qo~CQB!n->3bx&(jX;-G@`EygAMJ=b z*?*>Dq%FpYI_9DOKOI8CN#HQ(1t8E+z}=B?MO*Tf1Ogz#w#=-c>bIDV zt&pP<2cpt3V$4K4gFt`|s43dP$9HNezyStRS35|NNBAo2Es z(nTD%y9f-Y%3Tlm-)K5E3#J6ih=%4-#2qycb-4-@&r+_XC@+t51^8sZi!e4UI%!KN zBO+)I_DZDZG};qBfWJ*hted{M``h;6yF7K!c+qn&FDFE` z zpBb0Kxf(MrhjTUlm%1Fa5l2kCV=#Vv*y6Gzx0LVSN1iboobP@HB;#0(8JEGa7&9(o hT407{`G4&)7K{yUU3aaoVT@jpvcf+3q@729{V%jr @@ -64,7 +64,7 @@ style="font-size:32px;line-height:1.25;stroke-width:2.10014px" /> + transform="translate(-23.347973,0.557051)"> + - - - - - - align Arriba - SQUID + x="104.54099" + y="48.143513">StringTie pizzly + x="136.53439" + y="65.475723">Arriba - align kallisto + x="81.461792" + y="65.99041">align hardtrimming - fastpfastptrimming FusionCatcher align - - - - - - + sodipodi:nodetypes="ccc" /> STAR-Fusion FastQC Qualimap - MultiQC FusionInspector + x="220.19096" + y="96.223259">FusionInspector fusion-report + x="196.76082" + y="64.750114">fusion-report PicardPicard:CollectRnaSeqMetrics- CollectRnaSeqMetrics+- CollectWgsMetricsPicardCollectWgsMetrics - SQUIDannotate + id="tspan23187">- CollectInsertSizeMetrics - - - - + style="fill:none;stroke:#000000;stroke-width:2.42956;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 261.03273,71.013135 h -7.93593 c -3.94345,0 -7.79288,2.347188 -10.58131,4.962803 l -2.64532,2.481411" + id="path1232-7-2-7-3-7" + sodipodi:nodetypes="cssc" /> + + transform="translate(-10.236147,12.330532)"> qc Workflows: - - StringTie - + transform="translate(-9.762502,12.377831)"> - + sodipodi:nodetypes="ccc" /> squid - pizzly - stringtie - + y="147.22173">stringtie ArribaArribavisualisation + VCFcollect + + From 6fee33a833fb84bb5d6ebe2d0bcd4cffb375738c Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:55:01 +0100 Subject: [PATCH 087/175] fix typo --- subworkflows/local/arriba_workflow.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/arriba_workflow.nf b/subworkflows/local/arriba_workflow.nf index c4827407..3aa9c090 100644 --- a/subworkflows/local/arriba_workflow.nf +++ b/subworkflows/local/arriba_workflow.nf @@ -40,7 +40,7 @@ workflow ARRIBA_WORKFLOW { SAMTOOLS_SORT_FOR_ARRIBA(STAR_FOR_ARRIBA.out.bam) ch_versions = ch_versions.mix(SAMTOOLS_SORT_FOR_ARRIBA.out.versions ) - SAMTOOLS_VIEW_FOR_ARRIBA(SAMTOOLS_SORT_FOR_ARRIBA.bam.map { meta, bam -> [ meta, bam, [] ] }, ch_fasta, []) + SAMTOOLS_VIEW_FOR_ARRIBA(SAMTOOLS_SORT_FOR_ARRIBA.out.bam.map { meta, bam -> [ meta, bam, [] ] }, ch_fasta, []) ch_versions = ch_versions.mix(SAMTOOLS_VIEW_FOR_ARRIBA.out.versions ) SAMTOOLS_INDEX_FOR_ARRIBA(SAMTOOLS_VIEW_FOR_ARRIBA.out.cram) From 0b67a98b52decd09df5a3a05c0c69b5d25d56516 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:09:47 +0100 Subject: [PATCH 088/175] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3085346e..912ba695 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add picard CollectInserSizeMetrics to QC workflow [#408](https://github.com/nf-core/rnafusion/pull/408) +- Build CRAM index in the same directory as CRAM files for arriba and STAR-Fusion [#427](https://github.com/nf-core/rnafusion/pull/427) ### Changed From a9bfa7d634c60543931d52b431471e10307219d4 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:34:10 +0100 Subject: [PATCH 089/175] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9ff036..d56c52d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - cram file from output bam of `STAR_FOR_STARFUSION`: meta.id to meta.id.star_for_starfusion.Aligned.sortedByCoord.out - `fusion-report` index.html file to meta.id_fusionreport_index.html - meta.id.vcf output from `MEGAFUSION` to meta.id_fusion_data.vcf + - Update metro map [#428](https://github.com/nf-core/rnafusion/pull/428) ### Fixed From cc4ec30388dce42bdb7fd8ac1bbdbe1caf558f94 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 10:20:04 +0100 Subject: [PATCH 090/175] install agat/convertspgff2tsv from nf-core --- modules.json | 5 ++ .../agat/convertspgff2tsv/environment.yml | 6 ++ modules/nf-core/agat/convertspgff2tsv/main.nf | 35 +++++++++ .../nf-core/agat/convertspgff2tsv/meta.yml | 38 ++++++++++ modules/nf-core/picard/markduplicates/main.nf | 65 +++++++++++++++++ .../nf-core/picard/markduplicates/meta.yml | 71 +++++++++++++++++++ .../execution_trace_2023-11-01_15-05-02.txt | 1 + 7 files changed, 221 insertions(+) create mode 100644 modules/nf-core/agat/convertspgff2tsv/environment.yml create mode 100644 modules/nf-core/agat/convertspgff2tsv/main.nf create mode 100644 modules/nf-core/agat/convertspgff2tsv/meta.yml create mode 100644 modules/nf-core/picard/markduplicates/main.nf create mode 100644 modules/nf-core/picard/markduplicates/meta.yml create mode 100644 null/pipeline_info/execution_trace_2023-11-01_15-05-02.txt diff --git a/modules.json b/modules.json index 9805e2ef..beb0e954 100644 --- a/modules.json +++ b/modules.json @@ -5,6 +5,11 @@ "https://github.com/nf-core/modules.git": { "modules": { "nf-core": { + "agat/convertspgff2tsv": { + "branch": "master", + "git_sha": "53e6fd5d80141e00a3b70762f4361f6af1f4303b", + "installed_by": ["modules"] + }, "arriba": { "branch": "master", "git_sha": "ea9e2892a9d12e8769402f12096219942bcf6536", diff --git a/modules/nf-core/agat/convertspgff2tsv/environment.yml b/modules/nf-core/agat/convertspgff2tsv/environment.yml new file mode 100644 index 00000000..9ca0ea28 --- /dev/null +++ b/modules/nf-core/agat/convertspgff2tsv/environment.yml @@ -0,0 +1,6 @@ +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::agat=1.2.0 diff --git a/modules/nf-core/agat/convertspgff2tsv/main.nf b/modules/nf-core/agat/convertspgff2tsv/main.nf new file mode 100644 index 00000000..cef48360 --- /dev/null +++ b/modules/nf-core/agat/convertspgff2tsv/main.nf @@ -0,0 +1,35 @@ +process AGAT_CONVERTSPGFF2TSV { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/agat:1.2.0--pl5321hdfd78af_0' : + 'biocontainers/agat:1.2.0--pl5321hdfd78af_0' }" + + input: + tuple val(meta), path(gff) + + output: + tuple val(meta), path("*.tsv"), emit: tsv + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + agat_convert_sp_gff2tsv.pl \\ + --gff $gff \\ + --output ${prefix}.tsv \\ + $args + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + agat: \$(agat_convert_sp_gff2tsv.pl --help | sed '3!d; s/.*v//' | sed 's/ .*//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/agat/convertspgff2tsv/meta.yml b/modules/nf-core/agat/convertspgff2tsv/meta.yml new file mode 100644 index 00000000..f5865dfe --- /dev/null +++ b/modules/nf-core/agat/convertspgff2tsv/meta.yml @@ -0,0 +1,38 @@ +name: agat_convertspgff2tsv +description: | + Converts a GFF/GTF file into a TSV file +keywords: + - genome + - gff + - gtf + - conversion + - tsv +tools: + - agat: + description: "AGAT is a toolkit for manipulation and getting information from GFF/GTF files" + homepage: "https://github.com/NBISweden/AGAT" + documentation: "https://agat.readthedocs.io/" + tool_dev_url: "https://github.com/NBISweden/AGAT" + doi: "10.5281/zenodo.3552717" + licence: ["GPL v3"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - gff: + type: file + description: Annotation file in GFF3/GTF format + pattern: "*.{gff, gtf}" +output: + - tsv: + type: file + description: Annotation file in TSV format + pattern: "*.{gtf}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@rannick" diff --git a/modules/nf-core/picard/markduplicates/main.nf b/modules/nf-core/picard/markduplicates/main.nf new file mode 100644 index 00000000..ebfa0864 --- /dev/null +++ b/modules/nf-core/picard/markduplicates/main.nf @@ -0,0 +1,65 @@ +process PICARD_MARKDUPLICATES { + tag "$meta.id" + label 'process_medium' + + conda "bioconda::picard=3.0.0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/picard:3.0.0--hdfd78af_1' : + 'biocontainers/picard:3.0.0--hdfd78af_1' }" + + input: + tuple val(meta), path(bam) + tuple val(meta2), path(fasta) + tuple val(meta3), path(fai) + + output: + tuple val(meta), path("*.bam") , emit: bam + tuple val(meta), path("*.bai") , optional:true, emit: bai + tuple val(meta), path("*.metrics.txt"), emit: metrics + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def avail_mem = 3072 + if (!task.memory) { + log.info '[Picard MarkDuplicates] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.' + } else { + avail_mem = (task.memory.mega*0.8).intValue() + } + + if ("$bam" == "${prefix}.bam") error "Input and output names are the same, use \"task.ext.prefix\" to disambiguate!" + + """ + picard \\ + -Xmx${avail_mem}M \\ + MarkDuplicates \\ + $args \\ + --INPUT $bam \\ + --OUTPUT ${prefix}.bam \\ + --REFERENCE_SEQUENCE $fasta \\ + --METRICS_FILE ${prefix}.MarkDuplicates.metrics.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + picard: \$(echo \$(picard MarkDuplicates --version 2>&1) | grep -o 'Version:.*' | cut -f2- -d:) + END_VERSIONS + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + if ("$bam" == "${prefix}.bam") error "Input and output names are the same, use \"task.ext.prefix\" to disambiguate!" + """ + touch ${prefix}.bam + touch ${prefix}.bam.bai + touch ${prefix}.MarkDuplicates.metrics.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + picard: \$(echo \$(picard MarkDuplicates --version 2>&1) | grep -o 'Version:.*' | cut -f2- -d:) + END_VERSIONS + """ +} diff --git a/modules/nf-core/picard/markduplicates/meta.yml b/modules/nf-core/picard/markduplicates/meta.yml new file mode 100644 index 00000000..f7693d2f --- /dev/null +++ b/modules/nf-core/picard/markduplicates/meta.yml @@ -0,0 +1,71 @@ +name: picard_markduplicates +description: Locate and tag duplicate reads in a BAM file +keywords: + - markduplicates + - pcr + - duplicates + - bam + - sam + - cram +tools: + - picard: + description: | + A set of command line tools (in Java) for manipulating high-throughput sequencing (HTS) + data and formats such as SAM/BAM/CRAM and VCF. + homepage: https://broadinstitute.github.io/picard/ + documentation: https://broadinstitute.github.io/picard/ + licence: ["MIT"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - bam: + type: file + description: BAM file + pattern: "*.{bam,cram,sam}" + - meta2: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome' ] + - fasta: + type: file + description: Reference genome fasta file + pattern: "*.{fasta,fa}" + - meta3: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome' ] + - fai: + type: file + description: Reference genome fasta index + pattern: "*.{fai}" +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - bam: + type: file + description: BAM file with duplicate reads marked/removed + pattern: "*.{bam}" + - bai: + type: file + description: An optional BAM index file. If desired, --CREATE_INDEX must be passed as a flag + pattern: "*.{bai}" + - metrics: + type: file + description: Duplicate metrics file generated by picard + pattern: "*.{metrics.txt}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@drpatelh" + - "@projectoriented" + - "@ramprasadn" diff --git a/null/pipeline_info/execution_trace_2023-11-01_15-05-02.txt b/null/pipeline_info/execution_trace_2023-11-01_15-05-02.txt new file mode 100644 index 00000000..6b739acd --- /dev/null +++ b/null/pipeline_info/execution_trace_2023-11-01_15-05-02.txt @@ -0,0 +1 @@ +task_id hash native_id name status exit submit duration realtime %cpu peak_rss peak_vmem rchar wchar From 0c3ffaed91d6129217bdd6be8c9fddb002b70e21 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 10:27:33 +0100 Subject: [PATCH 091/175] correct typo --- subworkflows/local/fusioninspector_workflow.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/fusioninspector_workflow.nf b/subworkflows/local/fusioninspector_workflow.nf index 640b25fd..e616ee5b 100644 --- a/subworkflows/local/fusioninspector_workflow.nf +++ b/subworkflows/local/fusioninspector_workflow.nf @@ -1,4 +1,4 @@ -include { AGAT_CONVERTSPGFF2TSV } from '../../modules/nf-core/agat/visualisation/main' +include { AGAT_CONVERTSPGFF2TSV } from '../../modules/nf-core/agat/convertspgff2tsv/main' include { ARRIBA_VISUALISATION } from '../../modules/local/arriba/visualisation/main' include { CAT_CAT } from '../../modules/nf-core/cat/cat/main' include { VCF_COLLECT } from '../../modules/local/vcf_collect/main' From ba55fa3fdffda0b8c280e3807702bf4889b239eb Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:08:31 +0100 Subject: [PATCH 092/175] vcf_collect takes tsv_coding_effect channel instead of tsv --- modules/local/fusioninspector/main.nf | 17 +++++++++++------ subworkflows/local/fusioninspector_workflow.nf | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/local/fusioninspector/main.nf b/modules/local/fusioninspector/main.nf index 1b1a7169..11013837 100644 --- a/modules/local/fusioninspector/main.nf +++ b/modules/local/fusioninspector/main.nf @@ -10,10 +10,11 @@ process FUSIONINSPECTOR { path reference output: - tuple val(meta), path("*FusionInspector.fusions.tsv") , emit: tsv - tuple val(meta), path("*.gtf") , emit: out_gtf - path "*" , emit: output - path "versions.yml" , emit: versions + tuple val(meta), path("*FusionInspector.fusions.tsv") , emit: tsv + tuple val(meta), path("*FusionInspector.fusions.tsv.annotated.coding_effect") , emit: tsv_coding_effect + tuple val(meta), path("*.gtf") , emit: out_gtf + path "*" , emit: output + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -40,9 +41,13 @@ process FUSIONINSPECTOR { """ stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ - touch FusionInspector.log - touch FusionInspector.fusions.tsv + touch ${prefix}.FusionInspector.log + touch ${prefix}.FusionInspector.fusions.tsv + touch ${prefix}.FusionInspector.fusions.tsv.annotated.coding_effect + touch ${prefix}.gtf cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/subworkflows/local/fusioninspector_workflow.nf b/subworkflows/local/fusioninspector_workflow.nf index e616ee5b..32173e81 100644 --- a/subworkflows/local/fusioninspector_workflow.nf +++ b/subworkflows/local/fusioninspector_workflow.nf @@ -45,7 +45,7 @@ workflow FUSIONINSPECTOR_WORKFLOW { AGAT_CONVERTSPGFF2TSV(FUSIONINSPECTOR.out.out_gtf) ch_versions = ch_versions.mix(AGAT_CONVERTSPGFF2TSV.out.versions) - fusion_data = FUSIONINSPECTOR.out.tsv.join(AGAT_CONVERTSPGFF2TSV.out.tsv).join(fusionreport_out) + fusion_data = FUSIONINSPECTOR.out.tsv_coding_effect.join(AGAT_CONVERTSPGFF2TSV.out.tsv).join(fusionreport_out) VCF_COLLECT(fusion_data, ch_hgnc_ref, ch_hgnc_date) ch_versions = ch_versions.mix(VCF_COLLECT.out.versions) From 1b9c18115689e32039b56f40dbbd90731f572031 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:36:47 +0100 Subject: [PATCH 093/175] remove empty entry --- bin/vcf_collect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index ec08e432..cd268ac1 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -286,7 +286,7 @@ def column_manipulation(df): # INFO df.loc[index, "INFO"] = ( "SVTYPE=BND;CHRA={};CHRB={};GENEA={};GENEB={};POSA={};POSB={};ORIENTATION={},{};FOUND_DB={};" - "FOUND_IN={};;TOOL_HITS={};SCORE={};FRAME_STATUS={};TRANSCRIPT_ID_A={};TRANSCRIPT_ID_B={};" + "FOUND_IN={};TOOL_HITS={};SCORE={};FRAME_STATUS={};TRANSCRIPT_ID_A={};TRANSCRIPT_ID_B={};" "TRANSCRIPT_VERSION_A={};TRANSCRIPT_VERSION_B={};HGNC_ID_A={};HGNC_ID_B={};EXON_NUMBER_A={};" "EXON_NUMBER_B={};ANNOTATIONS={}".format( row["ChromosomeA"], From edc1a42d47f36814b1c5cf9af91ba5b46acd5de4 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:42:42 +0100 Subject: [PATCH 094/175] update arriba --- CHANGELOG.md | 1 + modules/local/arriba/download/main.nf | 20 ++++++++++---------- modules/local/arriba/visualisation/main.nf | 6 +++--- nextflow.config | 8 ++++---- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a071696..0e74c70b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - No more checks for existence of samplesheet, which made building references fail (building references uses a fake sample sheet if none is provided) [#420](https://github.com/nf-core/rnafusion/pull/420) - `--extreme_sensitivity` used for fusioninspector to minimize fusioninspector filtering [#424](https://github.com/nf-core/rnafusion/pull/424) - `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) +- `arriba` updated to 2.4.0 ### Fixed diff --git a/modules/local/arriba/download/main.nf b/modules/local/arriba/download/main.nf index 166ed69f..d59c123b 100644 --- a/modules/local/arriba/download/main.nf +++ b/modules/local/arriba/download/main.nf @@ -13,11 +13,11 @@ process ARRIBA_DOWNLOAD { script: """ - wget https://github.com/suhrig/arriba/releases/download/v2.3.0/arriba_v2.3.0.tar.gz -O arriba_v2.3.0.tar.gz - tar -xzvf arriba_v2.3.0.tar.gz - rm arriba_v2.3.0.tar.gz - mv arriba_v2.3.0/database/* . - rm -r arriba_v2.3.0 + wget https://github.com/suhrig/arriba/releases/download/v2.3.0/arriba_v2.4.0.tar.gz -O arriba_v2.4.0.tar.gz + tar -xzvf arriba_v2.4.0.tar.gz + rm arriba_v2.4.0.tar.gz + mv arriba_v2.4.0/database/* . + rm -r arriba_v2.4.0 cat <<-END_VERSIONS > versions.yml "${task.process}": @@ -27,11 +27,11 @@ process ARRIBA_DOWNLOAD { stub: """ - touch blacklist_hg38_GRCh38_v2.3.0.tsv.gz - touch protein_domains_hg38_GRCh38_v2.3.0.gff3 - touch cytobands_hg38_GRCh38_v2.3.0.tsv - touch known_fusions_hg38_GRCh38_v2.3.0.tsv.gz - touch protein_domains_hg38_GRCh38_v2.3.0.gff3 + touch blacklist_hg38_GRCh38_v2.4.0.tsv.gz + touch protein_domains_hg38_GRCh38_v2.4.0.gff3 + touch cytobands_hg38_GRCh38_v2.4.0.tsv + touch known_fusions_hg38_GRCh38_v2.4.0.tsv.gz + touch protein_domains_hg38_GRCh38_v2.4.0.gff3 cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/arriba/visualisation/main.nf b/modules/local/arriba/visualisation/main.nf index 5805a904..cc120119 100644 --- a/modules/local/arriba/visualisation/main.nf +++ b/modules/local/arriba/visualisation/main.nf @@ -2,10 +2,10 @@ process ARRIBA_VISUALISATION { tag "$meta.id" label 'process_medium' - conda "bioconda::arriba=2.3.0" + conda "bioconda::arriba=2.4.0" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/arriba:2.3.0--haa8aa89_0' : - 'quay.io/biocontainers/arriba:2.3.0--haa8aa89_0' }" + 'https://depot.galaxyproject.org/singularity/arriba:2.4.0--h0033a41_2' : + 'biocontainers/arriba:2.4.0--h0033a41_2' }" input: tuple val(meta), path(bam), path(bai), path(fusions) diff --git a/nextflow.config b/nextflow.config index 925d7a36..5f1e3c2c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -66,10 +66,10 @@ params { // Path to references ensembl_ref = "${params.genomes_base}/ensembl" arriba_ref = "${params.genomes_base}/arriba" - arriba_ref_blacklist = "${params.genomes_base}/arriba/blacklist_hg38_GRCh38_v2.3.0.tsv.gz" - arriba_ref_cytobands = "${params.genomes_base}/arriba/cytobands_hg38_GRCh38_v2.3.0.tsv" - arriba_ref_known_fusions = "${params.genomes_base}/arriba/known_fusions_hg38_GRCh38_v2.3.0.tsv.gz" - arriba_ref_protein_domains = "${params.genomes_base}/arriba/protein_domains_hg38_GRCh38_v2.3.0.gff3" + arriba_ref_blacklist = "${params.genomes_base}/arriba/blacklist_hg38_GRCh38_v2.4.0.tsv.gz" + arriba_ref_cytobands = "${params.genomes_base}/arriba/cytobands_hg38_GRCh38_v2.4.0.tsv" + arriba_ref_known_fusions = "${params.genomes_base}/arriba/known_fusions_hg38_GRCh38_v2.4.0.tsv.gz" + arriba_ref_protein_domains = "${params.genomes_base}/arriba/protein_domains_hg38_GRCh38_v2.4.0.gff3" fusioncatcher_ref = "${params.genomes_base}/fusioncatcher/human_v102" starfusion_ref = "${params.genomes_base}/starfusion/ctat_genome_lib_build_dir" starindex_ref = "${params.genomes_base}/star" From 775444e4fb7dc863b7f008a79632200e73b03498 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 15:15:23 +0100 Subject: [PATCH 095/175] make optional gtf and coding effectsg --- modules/local/fusioninspector/main.nf | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/modules/local/fusioninspector/main.nf b/modules/local/fusioninspector/main.nf index 11013837..c7fcd3f0 100644 --- a/modules/local/fusioninspector/main.nf +++ b/modules/local/fusioninspector/main.nf @@ -10,11 +10,11 @@ process FUSIONINSPECTOR { path reference output: - tuple val(meta), path("*FusionInspector.fusions.tsv") , emit: tsv - tuple val(meta), path("*FusionInspector.fusions.tsv.annotated.coding_effect") , emit: tsv_coding_effect - tuple val(meta), path("*.gtf") , emit: out_gtf - path "*" , emit: output - path "versions.yml" , emit: versions + tuple val(meta), path("*FusionInspector.fusions.tsv") , emit: tsv + tuple val(meta), path("*.coding_effect") , optional:true, emit: tsv_coding_effect + tuple val(meta), path("*.gtf") , optional:true, emit: out_gtf + path "*" , emit: output + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -42,7 +42,6 @@ process FUSIONINSPECTOR { stub: def prefix = task.ext.prefix ?: "${meta.id}" - """ touch ${prefix}.FusionInspector.log touch ${prefix}.FusionInspector.fusions.tsv From 4a38147d79fa7a59032c38c689b54034ad15e106 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 16:36:03 +0100 Subject: [PATCH 096/175] remove HGNC: from HGNC id column --- bin/vcf_collect.py | 1 + modules.json | 5 -- modules/nf-core/picard/markduplicates/main.nf | 65 ----------------- .../nf-core/picard/markduplicates/meta.yml | 71 ------------------- .../execution_trace_2023-11-01_15-05-02.txt | 1 - 5 files changed, 1 insertion(+), 142 deletions(-) delete mode 100644 modules/nf-core/picard/markduplicates/main.nf delete mode 100644 modules/nf-core/picard/markduplicates/meta.yml delete mode 100644 null/pipeline_info/execution_trace_2023-11-01_15-05-02.txt diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index cd268ac1..39da16ba 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -352,6 +352,7 @@ def build_hgnc_dataframe(file): Build a DataFrame from HGNC input file, extracting 'hgnc_id' and 'ensembl_gene_id' columns. """ df = pd.read_csv(file, sep="\t", low_memory=False) + df['hgnc_id'] = df['hgnc_id'].str.replace("HGNC:","") return df[["hgnc_id", "ensembl_gene_id"]].dropna() diff --git a/modules.json b/modules.json index beb0e954..a6ffdfd5 100644 --- a/modules.json +++ b/modules.json @@ -70,11 +70,6 @@ "git_sha": "735e1e04e7e01751d2d6e97055bbdb6f70683cc1", "installed_by": ["modules"] }, - "picard/markduplicates": { - "branch": "master", - "git_sha": "2ee934606f1fdf7fc1cb05d6e8abc13bec8ab448", - "installed_by": ["modules"] - }, "samtools/faidx": { "branch": "master", "git_sha": "fd742419940e01ba1c5ecb172c3e32ec840662fe", diff --git a/modules/nf-core/picard/markduplicates/main.nf b/modules/nf-core/picard/markduplicates/main.nf deleted file mode 100644 index ebfa0864..00000000 --- a/modules/nf-core/picard/markduplicates/main.nf +++ /dev/null @@ -1,65 +0,0 @@ -process PICARD_MARKDUPLICATES { - tag "$meta.id" - label 'process_medium' - - conda "bioconda::picard=3.0.0" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/picard:3.0.0--hdfd78af_1' : - 'biocontainers/picard:3.0.0--hdfd78af_1' }" - - input: - tuple val(meta), path(bam) - tuple val(meta2), path(fasta) - tuple val(meta3), path(fai) - - output: - tuple val(meta), path("*.bam") , emit: bam - tuple val(meta), path("*.bai") , optional:true, emit: bai - tuple val(meta), path("*.metrics.txt"), emit: metrics - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - def avail_mem = 3072 - if (!task.memory) { - log.info '[Picard MarkDuplicates] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.' - } else { - avail_mem = (task.memory.mega*0.8).intValue() - } - - if ("$bam" == "${prefix}.bam") error "Input and output names are the same, use \"task.ext.prefix\" to disambiguate!" - - """ - picard \\ - -Xmx${avail_mem}M \\ - MarkDuplicates \\ - $args \\ - --INPUT $bam \\ - --OUTPUT ${prefix}.bam \\ - --REFERENCE_SEQUENCE $fasta \\ - --METRICS_FILE ${prefix}.MarkDuplicates.metrics.txt - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - picard: \$(echo \$(picard MarkDuplicates --version 2>&1) | grep -o 'Version:.*' | cut -f2- -d:) - END_VERSIONS - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - if ("$bam" == "${prefix}.bam") error "Input and output names are the same, use \"task.ext.prefix\" to disambiguate!" - """ - touch ${prefix}.bam - touch ${prefix}.bam.bai - touch ${prefix}.MarkDuplicates.metrics.txt - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - picard: \$(echo \$(picard MarkDuplicates --version 2>&1) | grep -o 'Version:.*' | cut -f2- -d:) - END_VERSIONS - """ -} diff --git a/modules/nf-core/picard/markduplicates/meta.yml b/modules/nf-core/picard/markduplicates/meta.yml deleted file mode 100644 index f7693d2f..00000000 --- a/modules/nf-core/picard/markduplicates/meta.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: picard_markduplicates -description: Locate and tag duplicate reads in a BAM file -keywords: - - markduplicates - - pcr - - duplicates - - bam - - sam - - cram -tools: - - picard: - description: | - A set of command line tools (in Java) for manipulating high-throughput sequencing (HTS) - data and formats such as SAM/BAM/CRAM and VCF. - homepage: https://broadinstitute.github.io/picard/ - documentation: https://broadinstitute.github.io/picard/ - licence: ["MIT"] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM file - pattern: "*.{bam,cram,sam}" - - meta2: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'genome' ] - - fasta: - type: file - description: Reference genome fasta file - pattern: "*.{fasta,fa}" - - meta3: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'genome' ] - - fai: - type: file - description: Reference genome fasta index - pattern: "*.{fai}" -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM file with duplicate reads marked/removed - pattern: "*.{bam}" - - bai: - type: file - description: An optional BAM index file. If desired, --CREATE_INDEX must be passed as a flag - pattern: "*.{bai}" - - metrics: - type: file - description: Duplicate metrics file generated by picard - pattern: "*.{metrics.txt}" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@drpatelh" - - "@projectoriented" - - "@ramprasadn" diff --git a/null/pipeline_info/execution_trace_2023-11-01_15-05-02.txt b/null/pipeline_info/execution_trace_2023-11-01_15-05-02.txt deleted file mode 100644 index 6b739acd..00000000 --- a/null/pipeline_info/execution_trace_2023-11-01_15-05-02.txt +++ /dev/null @@ -1 +0,0 @@ -task_id hash native_id name status exit submit duration realtime %cpu peak_rss peak_vmem rchar wchar From 6f2a4ea24d3c6f1004e74fab5c93d898c5ebb1c5 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:06:07 +0100 Subject: [PATCH 097/175] fix hgnc date timestamping --- modules/local/hgnc/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/hgnc/main.nf b/modules/local/hgnc/main.nf index 7211cb71..1b3808f6 100644 --- a/modules/local/hgnc/main.nf +++ b/modules/local/hgnc/main.nf @@ -19,7 +19,7 @@ process HGNC_DOWNLOAD { script: """ wget https://ftp.ebi.ac.uk/pub/databases/genenames/hgnc/tsv/hgnc_complete_set.txt - date+%Y-%m-%d/%H:%M: > HGNC-DB-timestamp.txt + date +%Y-%m-%d/%H:%M > HGNC-DB-timestamp.txt cat <<-END_VERSIONS > versions.yml "${task.process}": From 0b4f60f6789c9ef1b1275aeb5f15f1a79f746644 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:09:47 +0100 Subject: [PATCH 098/175] fix typo --- modules/local/arriba/download/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/arriba/download/main.nf b/modules/local/arriba/download/main.nf index d59c123b..860439ad 100644 --- a/modules/local/arriba/download/main.nf +++ b/modules/local/arriba/download/main.nf @@ -13,7 +13,7 @@ process ARRIBA_DOWNLOAD { script: """ - wget https://github.com/suhrig/arriba/releases/download/v2.3.0/arriba_v2.4.0.tar.gz -O arriba_v2.4.0.tar.gz + wget https://github.com/suhrig/arriba/releases/download/v2.4.0/arriba_v2.4.0.tar.gz -O arriba_v2.4.0.tar.gz tar -xzvf arriba_v2.4.0.tar.gz rm arriba_v2.4.0.tar.gz mv arriba_v2.4.0/database/* . From f4c091214c983584bb933ec039a177455ab787c6 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:43:14 +0100 Subject: [PATCH 099/175] black --- bin/vcf_collect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 39da16ba..4b3ec199 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -352,7 +352,7 @@ def build_hgnc_dataframe(file): Build a DataFrame from HGNC input file, extracting 'hgnc_id' and 'ensembl_gene_id' columns. """ df = pd.read_csv(file, sep="\t", low_memory=False) - df['hgnc_id'] = df['hgnc_id'].str.replace("HGNC:","") + df["hgnc_id"] = df["hgnc_id"].str.replace("HGNC:", "") return df[["hgnc_id", "ensembl_gene_id"]].dropna() From a641b172522408cae7060e5b2b22ed1b65f773ed Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:47:17 +0100 Subject: [PATCH 100/175] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e74c70b..c9dcad59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - No more checks for existence of samplesheet, which made building references fail (building references uses a fake sample sheet if none is provided) [#420](https://github.com/nf-core/rnafusion/pull/420) - `--extreme_sensitivity` used for fusioninspector to minimize fusioninspector filtering [#424](https://github.com/nf-core/rnafusion/pull/424) - `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) -- `arriba` updated to 2.4.0 +- `Arriba` updated to 2.4.0 [#429](https://github.com/nf-core/rnafusion/pull/429) ### Fixed From 4337f4ae1653361393190618077291b14d08ca76 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Tue, 7 Nov 2023 21:49:47 +0100 Subject: [PATCH 101/175] add support for case fusioninspector filters out a fusion from fusionreport --- bin/vcf_collect.py | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 4b3ec199..408abfe9 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -11,7 +11,7 @@ logger = logging.getLogger() -def vcf_collect(fusioninspector_in_file, fusionreport_in_file, gtf, hgnc, sample, out): +def vcf_collect(fusioninspector_in_file, fusionreport_in_file, sample, hgnc, gtf, out): """ Process FusionInspector and FusionReport data, merge with GTF from FusionInspector and HGNC database, @@ -33,14 +33,17 @@ def vcf_collect(fusioninspector_in_file, fusionreport_in_file, gtf, hgnc, sample .reset_index() ) - df = build_hgnc_dataframe(hgnc).merge( + df = build_hgcn_dataframe(hgnc).merge( merged_df, how="right", left_on="ensembl_gene_id", right_on="Left_ensembl_gene_id" ) df = df.rename(columns={"hgnc_id": "Left_hgnc_id"}) - df = build_hgnc_dataframe(hgnc).merge(df, how="right", left_on="ensembl_gene_id", right_on="Right_ensembl_gene_id") + df = build_hgcn_dataframe(hgnc).merge(df, how="right", left_on="ensembl_gene_id", right_on="Right_ensembl_gene_id") df = df.rename(columns={"hgnc_id": "Right_hgnc_id"}) gtf_df = build_gtf_dataframe(gtf) all_df = df.merge(gtf_df, how="left", left_on="CDS_LEFT_ID", right_on="Transcript_id") + all_df[["PosA", "orig_start", "orig_end"]] = all_df[["PosA", "orig_start", "orig_end"]].fillna(0) + all_df[["PosA", "orig_start", "orig_end"]] = all_df[["PosA", "orig_start", "orig_end"]].astype(int) + all_df = all_df[(all_df["PosA"] >= all_df["orig_start"]) & (all_df["PosA"] <= all_df["orig_end"])] all_df = all_df.rename(columns={"transcript_version": "Left_transcript_version"}) all_df = all_df.rename(columns={"exon_number": "Left_exon_number"}) @@ -73,7 +76,10 @@ def vcf_collect(fusioninspector_in_file, fusionreport_in_file, gtf, hgnc, sample ] ].drop_duplicates() all_df = all_df.merge(gtf_df, how="left", left_on="CDS_RIGHT_ID", right_on="Transcript_id") + all_df[["PosB", "orig_start", "orig_end"]] = all_df[["PosB", "orig_start", "orig_end"]].fillna(0) + all_df[["PosB", "orig_start", "orig_end"]] = all_df[["PosB", "orig_start", "orig_end"]].astype(int) all_df = all_df[(all_df["PosB"] >= all_df["orig_start"]) & (all_df["PosB"] <= all_df["orig_end"])] + all_df = all_df.rename(columns={"transcript_version": "Right_transcript_version"}) all_df = all_df.rename(columns={"exon_number": "Right_exon_number"}) all_df = all_df[ @@ -195,7 +201,6 @@ def build_fusioninspector_dataframe(file): df = df.rename(columns={"#FusionName": "FUSION"}) df[["ChromosomeA", "PosA", "Strand1"]] = df["LeftBreakpoint"].str.split(":", expand=True) df[["ChromosomeB", "PosB", "Strand2"]] = df["RightBreakpoint"].str.split(":", expand=True) - df[["GeneA", "GeneB"]] = df["FUSION"].str.split("--", expand=True) df[["LeftGeneName", "Left_ensembl_gene_id"]] = df["LeftGene"].str.split("^", expand=True) df[["RightGeneName", "Right_ensembl_gene_id"]] = df["RightGene"].str.split("^", expand=True) return df.set_index(["FUSION"]) @@ -234,11 +239,11 @@ def read_build_fusionreport(fusionreport_file): from_html = [line.split('rows": [')[1] for line in f if 'name="fusion_list' in line] expression = from_html[0].split('], "tool')[0] fusion_report = pd.DataFrame.from_dict(ast.literal_eval(expression)) - if "arriba" not in fusion_report.columns: + if not "arriba" in fusion_report.columns: fusion_report["arriba"] = "" - if "fusioncatcher" not in fusion_report.columns: + if not "fusioncatcher" in fusion_report.columns: fusion_report["fusioncatcher"] = "" - if "starfusion" not in fusion_report.columns: + if not "starfusion" in fusion_report.columns: fusion_report["starfusion"] = "" fusion_report["arriba"] = fusion_report[["arriba"]].apply( replace_value_with_column_name, args=("true", "arriba"), axis=1 @@ -254,7 +259,11 @@ def read_build_fusionreport(fusionreport_file): ) fusion_report.columns = fusion_report.columns.str.upper() fusion_report["FOUND_DB"] = fusion_report["FOUND_DB"].apply(lambda x: ", ".join(x)) - return fusion_report[["FUSION", "TOOLS_HITS", "SCORE", "FOUND_DB", "FOUND_IN"]].set_index(["FUSION"]) + fusion_report[["GeneA", "GeneB"]] = fusion_report["FUSION"].str.split("--", expand=True) + + return fusion_report[["FUSION", "GeneA", "GeneB", "TOOLS_HITS", "SCORE", "FOUND_DB", "FOUND_IN"]].set_index( + ["FUSION"] + ) def column_manipulation(df): @@ -270,10 +279,12 @@ def column_manipulation(df): df["REF"] = "N" df["INFO"] = "" df["Sample"] = "" + df["Strand1"] = df["Strand1"].astype(str) for index, row in df.iterrows(): - # ALT - if not row["Strand1"] in ["+", "-"] or not row["Strand2"] in ["+", "-"]: + if row["Strand1"] == "nan": + df.loc[index, "ALT"] = "nan" + elif not row["Strand1"] in ["+", "-"] or not row["Strand2"] in ["+", "-"]: df.loc[index, "ALT"] = "N[{}:{}[".format(df["ChromosomeB"], row["PosB"]) elif row["Strand1"] == "-" and row["Strand2"] == "-": df.loc[index, "ALT"] = "[{}:{}[N".format(row["ChromosomeB"], row["PosB"]) @@ -283,12 +294,12 @@ def column_manipulation(df): df.loc[index, "ALT"] = "N]{}:{}]".format(row["ChromosomeB"], row["PosB"]) else: df.loc[index, "ALT"] = "N[{}:{}[".format(row["ChromosomeB"], row["PosB"]) - # INFO + df.loc[index, "INFO"] = ( "SVTYPE=BND;CHRA={};CHRB={};GENEA={};GENEB={};POSA={};POSB={};ORIENTATION={},{};FOUND_DB={};" - "FOUND_IN={};TOOL_HITS={};SCORE={};FRAME_STATUS={};TRANSCRIPT_ID_A={};TRANSCRIPT_ID_B={};" - "TRANSCRIPT_VERSION_A={};TRANSCRIPT_VERSION_B={};HGNC_ID_A={};HGNC_ID_B={};EXON_NUMBER_A={};" - "EXON_NUMBER_B={};ANNOTATIONS={}".format( + "FOUND_IN={};;TOOL_HITS={};SCORE={};FRAME_STATUS={};TRANSCRIPT_ID_A={};TRANSCRIPT_ID_B={};" + "TRANSCRIPT_VERSION_A={};TRANSCRIPT_VERSION_B={};HGNC_ID_A={};HGNC_ID_B={};EXON_NUMBER_A={},EXON_NUMBER_B={};" + "ANNOTATIONS={}".format( row["ChromosomeA"], row["ChromosomeB"], row["GeneA"], @@ -347,7 +358,7 @@ def write_vcf(df_to_print, header, out_file): f.write(header.rstrip("\r\n") + "\n" + content) -def build_hgnc_dataframe(file): +def build_hgcn_dataframe(file): """ Build a DataFrame from HGNC input file, extracting 'hgnc_id' and 'ensembl_gene_id' columns. """ From a6c995f6c3313c176f269302173beca532344bb9 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Tue, 7 Nov 2023 22:28:24 +0100 Subject: [PATCH 102/175] update ci --- .github/workflows/ci.yml | 48 +++++++--------------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17f01186..09f52fe5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,9 @@ jobs: NXF_VER: - "23.04.0" - "latest-everything" + trim_parameters: + - "--fastp_trim false" + - "--fastp_trim true" steps: - name: Check out pipeline code uses: actions/checkout@v3 @@ -35,51 +38,16 @@ jobs: with: version: "${{ matrix.NXF_VER }}" - - name: Dry test arriba build + - name: Dry test build run: | nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub --build_references \ - --outdir /home/runner/work/rnafusion/rnafusion/results --arriba \ + --outdir /home/runner/work/rnafusion/rnafusion/results --all \ --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - name: Dry test arriba + - name: Dry test run run: | nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub \ - --outdir /home/runner/work/rnafusion/rnafusion/results --arriba \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - - name: Dry test fusioncatcher build - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub --build_references \ - --outdir /home/runner/work/rnafusion/rnafusion/results --fusioncatcher \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} + --outdir /home/runner/work/rnafusion/rnafusion/results --all matrix.trim_parameters \ + --genomes_base /home/runner/work/rnafusion/rnafusion/results/references - - name: Dry test fusioncatcher - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub \ - --outdir /home/runner/work/rnafusion/rnafusion/results --fusioncatcher \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - - name: Dry test starfusion build - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub build_references \ - --outdir /home/runner/work/rnafusion/rnafusion/results --starfusion \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - - name: Dry test starfusion - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub \ - --outdir /home/runner/work/rnafusion/rnafusion/results --starfusion \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} - - - name: Dry test stingtie - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub \ - --outdir /home/runner/work/rnafusion/rnafusion/results --stringtie \ - --genomes_base /home/runner/work/rnafusion/rnafusion/results/references \ - --cosmic_username ${{ secrets.COSMIC_USERNAME }} --cosmic_passwd ${{ secrets.COSMIC_PASSWD }} From e8b58d4fbcb481e2913852f696689917d0a736aa Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Tue, 7 Nov 2023 22:30:25 +0100 Subject: [PATCH 103/175] prettier --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09f52fe5..33f320ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,8 +27,8 @@ jobs: - "23.04.0" - "latest-everything" trim_parameters: - - "--fastp_trim false" - - "--fastp_trim true" + - "--fastp_trim false" + - "--fastp_trim true" steps: - name: Check out pipeline code uses: actions/checkout@v3 @@ -50,4 +50,3 @@ jobs: nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub \ --outdir /home/runner/work/rnafusion/rnafusion/results --all matrix.trim_parameters \ --genomes_base /home/runner/work/rnafusion/rnafusion/results/references - From 50ab173f0db4ec9549724123ddbea644d8e5d5ab Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Tue, 7 Nov 2023 22:36:21 +0100 Subject: [PATCH 104/175] make matrix a parameter not a string --- .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 33f320ec..6f960cad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,5 +48,5 @@ jobs: - name: Dry test run run: | nextflow run ${GITHUB_WORKSPACE} -profile test,docker -stub \ - --outdir /home/runner/work/rnafusion/rnafusion/results --all matrix.trim_parameters \ + --outdir /home/runner/work/rnafusion/rnafusion/results --all ${{ matrix.trim_parameters }} \ --genomes_base /home/runner/work/rnafusion/rnafusion/results/references From 7b8cc873ef98b3a14dd16ddd02b90203137c8868 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Tue, 7 Nov 2023 22:57:37 +0100 Subject: [PATCH 105/175] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9dcad59..42489291 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--extreme_sensitivity` used for fusioninspector to minimize fusioninspector filtering [#424](https://github.com/nf-core/rnafusion/pull/424) - `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) - `Arriba` updated to 2.4.0 [#429](https://github.com/nf-core/rnafusion/pull/429) +- CI tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#430](https://github.com/nf-core/rnafusion/pull/430) ### Fixed From fb70a6b194969b7b9dead82334673985fec44107 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:58:39 +0100 Subject: [PATCH 106/175] update fusionreport to 2.1.5p6 --- modules/local/fusionreport/detect/main.nf | 2 +- modules/local/fusionreport/download/main.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/local/fusionreport/detect/main.nf b/modules/local/fusionreport/detect/main.nf index ac03bded..656334c4 100644 --- a/modules/local/fusionreport/detect/main.nf +++ b/modules/local/fusionreport/detect/main.nf @@ -4,7 +4,7 @@ process FUSIONREPORT { // Note: 2.7X indices incompatible with AWS iGenomes. conda "bioconda::star=2.7.9a" - container "docker.io/clinicalgenomics/fusion-report:2.1.5p5" + container "docker.io/clinicalgenomics/fusion-report:2.1.5p6" input: diff --git a/modules/local/fusionreport/download/main.nf b/modules/local/fusionreport/download/main.nf index e18955cc..1632b2cf 100644 --- a/modules/local/fusionreport/download/main.nf +++ b/modules/local/fusionreport/download/main.nf @@ -4,7 +4,7 @@ process FUSIONREPORT_DOWNLOAD { // Note: 2.7X indices incompatible with AWS iGenomes. conda "bioconda::star=2.7.9a" - container "docker.io/clinicalgenomics/fusion-report:2.1.5p5" + container "docker.io/clinicalgenomics/fusion-report:2.1.5p6" input: val(username) From bcd0fa8fb921a6145e612ffa8d263f8c5a6b6021 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 8 Nov 2023 12:01:28 +0100 Subject: [PATCH 107/175] remove unneccessary comments --- modules/local/fusionreport/detect/main.nf | 1 - modules/local/fusionreport/download/main.nf | 1 - 2 files changed, 2 deletions(-) diff --git a/modules/local/fusionreport/detect/main.nf b/modules/local/fusionreport/detect/main.nf index 656334c4..8b7b55af 100644 --- a/modules/local/fusionreport/detect/main.nf +++ b/modules/local/fusionreport/detect/main.nf @@ -2,7 +2,6 @@ process FUSIONREPORT { tag "$meta.id" label 'process_medium' - // Note: 2.7X indices incompatible with AWS iGenomes. conda "bioconda::star=2.7.9a" container "docker.io/clinicalgenomics/fusion-report:2.1.5p6" diff --git a/modules/local/fusionreport/download/main.nf b/modules/local/fusionreport/download/main.nf index 1632b2cf..500e11b5 100644 --- a/modules/local/fusionreport/download/main.nf +++ b/modules/local/fusionreport/download/main.nf @@ -2,7 +2,6 @@ process FUSIONREPORT_DOWNLOAD { tag 'fusionreport' label 'process_medium' - // Note: 2.7X indices incompatible with AWS iGenomes. conda "bioconda::star=2.7.9a" container "docker.io/clinicalgenomics/fusion-report:2.1.5p6" From 457bdac8759d97fb3cbea1b65ebe3a5991969432 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 8 Nov 2023 12:03:09 +0100 Subject: [PATCH 108/175] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9dcad59..46cad4da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--extreme_sensitivity` used for fusioninspector to minimize fusioninspector filtering [#424](https://github.com/nf-core/rnafusion/pull/424) - `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) - `Arriba` updated to 2.4.0 [#429](https://github.com/nf-core/rnafusion/pull/429) +- Update `fusion-report` to 2.1.5p6 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) ### Fixed From be99aef3a95573574dc9a9bac357dc20fac66f5d Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:14:40 +0100 Subject: [PATCH 109/175] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9dcad59..81535f94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--extreme_sensitivity` used for fusioninspector to minimize fusioninspector filtering [#424](https://github.com/nf-core/rnafusion/pull/424) - `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) - `Arriba` updated to 2.4.0 [#429](https://github.com/nf-core/rnafusion/pull/429) +- Change megafusion into vcf_collect, taking into account e.g. the annotation and coding effects outputs from fusioninspector, HGNC ids, frame status... [#414](https://github.com/nf-core/rnafusion/pull/414) ### Fixed From 72ed5b136d2ec30091a556ff3f7a541bbac09c73 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:40:20 +0100 Subject: [PATCH 110/175] update awstest --- .github/workflows/awstest.yml | 128 +--------------------------------- .nf-test.log | 2 + 2 files changed, 5 insertions(+), 125 deletions(-) create mode 100644 .nf-test.log diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml index d43fe90b..b72a293b 100644 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: # Launch workflow using Tower CLI tool action - - name: Launch build arriba workflow via tower + - name: Launch build references workflow via tower uses: seqeralabs/action-tower-launch@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} @@ -25,7 +25,7 @@ jobs: "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", "cosmic_username": "${{ secrets.cosmic_username }}", "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "arriba": true, + "all": true, "stub": true, "build_references": true } @@ -50,129 +50,7 @@ jobs: "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", "cosmic_username": "${{ secrets.cosmic_username }}", "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "arriba": true, - "stub": true - } - profiles: test,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch build starfusion workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "starfusion": true, - "build_references": true, - "stub": true - } - profiles: test,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch starfusion workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "starfusion": true, - "stub": true - } - profiles: test,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch build fusioncatcher workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "fusioncatcher": true, - "build_references": true, - "stub": true - } - profiles: test,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch fusioncatcher workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "fusioncatcher": true, - "stub": true - } - profiles: test,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch stringtie workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "stringtie": true, + "all": true, "stub": true } profiles: test,aws_tower diff --git a/.nf-test.log b/.nf-test.log new file mode 100644 index 00000000..d46827bf --- /dev/null +++ b/.nf-test.log @@ -0,0 +1,2 @@ +Nov-08 13:32:43.869 [main] INFO com.askimed.nf.test.App - nf-test 0.8.1 +Nov-08 13:32:43.902 [main] INFO com.askimed.nf.test.App - Arguments: [version] From 867626731b7ef6712934a2c1dfedbe1ba093a090 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:46:55 +0100 Subject: [PATCH 111/175] remove nf-test log --- .nf-test.log | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .nf-test.log diff --git a/.nf-test.log b/.nf-test.log deleted file mode 100644 index d46827bf..00000000 --- a/.nf-test.log +++ /dev/null @@ -1,2 +0,0 @@ -Nov-08 13:32:43.869 [main] INFO com.askimed.nf.test.App - nf-test 0.8.1 -Nov-08 13:32:43.902 [main] INFO com.askimed.nf.test.App - Arguments: [version] From 262546d20f2c470dd78f85d803548591a3bd872d Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:55:33 +0100 Subject: [PATCH 112/175] update awsfulltest --- .github/workflows/awsfulltest.yml | 131 +----------------------------- 1 file changed, 4 insertions(+), 127 deletions(-) diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index f25848e7..d2c448d7 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -13,7 +13,7 @@ jobs: if: github.repository == 'nf-core/rnafusion' runs-on: ubuntu-latest steps: - - name: Launch build arriba workflow via tower + - name: Launch build references workflow via tower uses: seqeralabs/action-tower-launch@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} @@ -28,7 +28,7 @@ jobs: "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", "cosmic_username": "${{ secrets.cosmic_username }}", "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "arriba": true, + "all": true, "build_references": true } profiles: test_full,aws_tower @@ -39,7 +39,7 @@ jobs: tower_action_*.log tower_action_*.json - - name: Launch arriba workflow via tower + - name: Launch run workflow via tower uses: seqeralabs/action-tower-launch@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} @@ -52,129 +52,6 @@ jobs: "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", "cosmic_username": "${{ secrets.cosmic_username }}", "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "arriba": true, + "all": true, } profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch build starfusion workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "starfusion": true, - "build_references": true - } - profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch starfusion workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "starfusion": true, - } - profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch build fusioncatcher workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "fusioncatcher": true, - "build_references": true - } - profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch fusioncatcher workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "fusioncatcher": true, - } - profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json - - - name: Launch stringtie workflow via tower - uses: seqeralabs/action-tower-launch@v2 - with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/rnafusion/work-${{ github.sha }} - parameters: | - { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}", - "genomes_base": "s3://${{ secrets.AWS_S3_BUCKET }}/rnafusion/results-${{ github.sha }}/references", - "cosmic_username": "${{ secrets.cosmic_username }}", - "cosmic_passwd": "${{ secrets.cosmic_passwd }}", - "stringtie": true, - } - profiles: test_full,aws_tower - - uses: actions/upload-artifact@v3 - with: - name: Tower debug log file - path: | - tower_action_*.log - tower_action_*.json From c472f63c23c3dd033ca7d5b6fea315b2f5fcaaf4 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:10:46 +0100 Subject: [PATCH 113/175] update fusionreport --- CHANGELOG.md | 2 +- modules/local/fusionreport/detect/main.nf | 2 +- modules/local/fusionreport/download/main.nf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46cad4da..058b8459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--extreme_sensitivity` used for fusioninspector to minimize fusioninspector filtering [#424](https://github.com/nf-core/rnafusion/pull/424) - `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) - `Arriba` updated to 2.4.0 [#429](https://github.com/nf-core/rnafusion/pull/429) -- Update `fusion-report` to 2.1.5p6 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) +- Update `fusion-report` to 2.1.5p7 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) and rapidjson []() ### Fixed diff --git a/modules/local/fusionreport/detect/main.nf b/modules/local/fusionreport/detect/main.nf index 8b7b55af..447bf528 100644 --- a/modules/local/fusionreport/detect/main.nf +++ b/modules/local/fusionreport/detect/main.nf @@ -3,7 +3,7 @@ process FUSIONREPORT { label 'process_medium' conda "bioconda::star=2.7.9a" - container "docker.io/clinicalgenomics/fusion-report:2.1.5p6" + container "docker.io/clinicalgenomics/fusion-report:2.1.5p7" input: diff --git a/modules/local/fusionreport/download/main.nf b/modules/local/fusionreport/download/main.nf index 500e11b5..9d529426 100644 --- a/modules/local/fusionreport/download/main.nf +++ b/modules/local/fusionreport/download/main.nf @@ -3,7 +3,7 @@ process FUSIONREPORT_DOWNLOAD { label 'process_medium' conda "bioconda::star=2.7.9a" - container "docker.io/clinicalgenomics/fusion-report:2.1.5p6" + container "docker.io/clinicalgenomics/fusion-report:2.1.5p7" input: val(username) From 65be55e441b7bd17f4f2206a8a05db1bb33f8ea1 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:11:41 +0100 Subject: [PATCH 114/175] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 058b8459..f23716b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--extreme_sensitivity` used for fusioninspector to minimize fusioninspector filtering [#424](https://github.com/nf-core/rnafusion/pull/424) - `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) - `Arriba` updated to 2.4.0 [#429](https://github.com/nf-core/rnafusion/pull/429) -- Update `fusion-report` to 2.1.5p7 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) and rapidjson []() +- Update `fusion-report` to 2.1.5p7 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) and rapidjson [#434](https://github.com/nf-core/rnafusion/pull/434) ### Fixed From 0a9eacfcc0562d41c8b86cac0d583e12a9b14964 Mon Sep 17 00:00:00 2001 From: Eva Caceres Date: Thu, 9 Nov 2023 12:28:22 +0100 Subject: [PATCH 115/175] update container for fusionreport --- modules/local/fusionreport/detect/main.nf | 2 +- modules/local/fusionreport/download/main.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/local/fusionreport/detect/main.nf b/modules/local/fusionreport/detect/main.nf index 447bf528..6060e0ae 100644 --- a/modules/local/fusionreport/detect/main.nf +++ b/modules/local/fusionreport/detect/main.nf @@ -3,7 +3,7 @@ process FUSIONREPORT { label 'process_medium' conda "bioconda::star=2.7.9a" - container "docker.io/clinicalgenomics/fusion-report:2.1.5p7" + container "docker.io/clinicalgenomics/fusion-report:2.1.5p8" input: diff --git a/modules/local/fusionreport/download/main.nf b/modules/local/fusionreport/download/main.nf index 9d529426..220b51c9 100644 --- a/modules/local/fusionreport/download/main.nf +++ b/modules/local/fusionreport/download/main.nf @@ -3,7 +3,7 @@ process FUSIONREPORT_DOWNLOAD { label 'process_medium' conda "bioconda::star=2.7.9a" - container "docker.io/clinicalgenomics/fusion-report:2.1.5p7" + container "docker.io/clinicalgenomics/fusion-report:2.1.5p8" input: val(username) From 7118cd04d59b6478a5972b6006c960d3193aa3c4 Mon Sep 17 00:00:00 2001 From: Eva Caceres Date: Thu, 9 Nov 2023 12:31:47 +0100 Subject: [PATCH 116/175] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f23716b1..b28d1a22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--extreme_sensitivity` used for fusioninspector to minimize fusioninspector filtering [#424](https://github.com/nf-core/rnafusion/pull/424) - `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) - `Arriba` updated to 2.4.0 [#429](https://github.com/nf-core/rnafusion/pull/429) -- Update `fusion-report` to 2.1.5p7 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) and rapidjson [#434](https://github.com/nf-core/rnafusion/pull/434) +- Update `fusion-report` to 2.1.5p8 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432), rapidjson [#434](https://github.com/nf-core/rnafusion/pull/434) and logs [#435](https://github.com/nf-core/rnafusion/pull/435) ### Fixed From 1c109ffcdfb765f49df852636e9bde4b331246e9 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 10 Nov 2023 10:34:00 +0100 Subject: [PATCH 117/175] Update bin/vcf_collect.py Co-authored-by: Eva C <29628428+fevac@users.noreply.github.com> --- bin/vcf_collect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 408abfe9..69d39f34 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -193,7 +193,7 @@ def header_def(sample): ) -def build_fusioninspector_dataframe(file): +def build_fusioninspector_dataframe(file: str) -> pd.DataFrame: """ Read FusionInspector output from a CSV file, preprocess the data, and set 'FUSION' as the index. """ From ff51b1cee25120fc583c9b20ebba37620ac91359 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 10 Nov 2023 10:36:44 +0100 Subject: [PATCH 118/175] Apply suggestions from code review Co-authored-by: Eva C <29628428+fevac@users.noreply.github.com> --- bin/vcf_collect.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 69d39f34..7d43a98a 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -11,7 +11,7 @@ logger = logging.getLogger() -def vcf_collect(fusioninspector_in_file, fusionreport_in_file, sample, hgnc, gtf, out): +def vcf_collect(fusioninspector_in_file: str, fusionreport_in_file: str, sample: str, hgnc: str, gtf: str, out_file) -> None: """ Process FusionInspector and FusionReport data, merge with GTF from FusionInspector and HGNC database, @@ -206,7 +206,7 @@ def build_fusioninspector_dataframe(file: str) -> pd.DataFrame: return df.set_index(["FUSION"]) -def replace_value_with_column_name(row, value_to_replace, column_name): +def replace_value_with_column_name(row: pd.Series, value_to_replace: str, column_name: str) -> str: """ Replace a specific value in a row with the corresponding column name. """ @@ -220,7 +220,7 @@ def replace_value_with_column_name(row, value_to_replace, column_name): return new_values -def concatenate_columns(row): +def concatenate_columns(row: pd.Series) -> str: """ Concatenate non-empty values in a row into a single string separated by commas. """ @@ -228,7 +228,7 @@ def concatenate_columns(row): return ",".join(non_empty_values) -def read_build_fusionreport(fusionreport_file): +def read_build_fusionreport(fusionreport_file: str) -> pd.DataFrame: """ Read and preprocess fusion-report data from a file, including handling missing tool columns, getting the columns with each tool and create a new FOUND_IN column with all the tool hits. @@ -266,7 +266,7 @@ def read_build_fusionreport(fusionreport_file): ) -def column_manipulation(df): +def column_manipulation(df: pd.DataFrame) -> pd.DataFrame: """ Manipulate and prepare DataFrame for VCF file creation. """ @@ -297,7 +297,7 @@ def column_manipulation(df): df.loc[index, "INFO"] = ( "SVTYPE=BND;CHRA={};CHRB={};GENEA={};GENEB={};POSA={};POSB={};ORIENTATION={},{};FOUND_DB={};" - "FOUND_IN={};;TOOL_HITS={};SCORE={};FRAME_STATUS={};TRANSCRIPT_ID_A={};TRANSCRIPT_ID_B={};" + "FOUND_IN={};TOOL_HITS={};SCORE={};FRAME_STATUS={};TRANSCRIPT_ID_A={};TRANSCRIPT_ID_B={};" "TRANSCRIPT_VERSION_A={};TRANSCRIPT_VERSION_B={};HGNC_ID_A={};HGNC_ID_B={};EXON_NUMBER_A={},EXON_NUMBER_B={};" "ANNOTATIONS={}".format( row["ChromosomeA"], @@ -328,7 +328,7 @@ def column_manipulation(df): return df -def write_vcf(df_to_print, header, out_file): +def write_vcf(df_to_print: pd.DataFrame, header: str, out_file: str) -> None: """ Write a VCF file with a specified DataFrame, header, and output file path. """ @@ -358,7 +358,7 @@ def write_vcf(df_to_print, header, out_file): f.write(header.rstrip("\r\n") + "\n" + content) -def build_hgcn_dataframe(file): +def build_hgcn_dataframe(file: str) -> pd.DataFrame: """ Build a DataFrame from HGNC input file, extracting 'hgnc_id' and 'ensembl_gene_id' columns. """ @@ -367,7 +367,7 @@ def build_hgcn_dataframe(file): return df[["hgnc_id", "ensembl_gene_id"]].dropna() -def build_gtf_dataframe(file): +def build_gtf_dataframe(file: str) -> pd.DataFrame: """ Build a DataFrame from GTF file converted in TSV, extracting relevant columns. """ From de3a105937a8d54ac75dc893ac56b70288156c57 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 09:32:20 +0100 Subject: [PATCH 119/175] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f87f5165..f4382e81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) - `Arriba` updated to 2.4.0 [#429](https://github.com/nf-core/rnafusion/pull/429) - CI tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#430](https://github.com/nf-core/rnafusion/pull/430) +- AWS tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#433](https://github.com/nf-core/rnafusion/pull/433) - Update `fusion-report` to 2.1.5p8 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) ### Fixed From 92a3dadf2b80eb151746c5217b37d6d44328d953 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:04:16 +0100 Subject: [PATCH 120/175] use fstrings --- bin/vcf_collect.py | 47 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 7d43a98a..c5157512 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -285,46 +285,27 @@ def column_manipulation(df: pd.DataFrame) -> pd.DataFrame: if row["Strand1"] == "nan": df.loc[index, "ALT"] = "nan" elif not row["Strand1"] in ["+", "-"] or not row["Strand2"] in ["+", "-"]: - df.loc[index, "ALT"] = "N[{}:{}[".format(df["ChromosomeB"], row["PosB"]) + df.loc[index, "ALT"] = f'N[{df["ChromosomeB"]}:{row["PosB"]}[' elif row["Strand1"] == "-" and row["Strand2"] == "-": - df.loc[index, "ALT"] = "[{}:{}[N".format(row["ChromosomeB"], row["PosB"]) + df.loc[index, "ALT"] = f'[{row["ChromosomeB"]}:{row["PosB"]}[N' elif row["Strand1"] == "+" and row["Strand2"] == "-": - df.loc[index, "ALT"] = "N]{}:{}]".format(row["ChromosomeB"], row["PosB"]) + df.loc[index, "ALT"] = f'N]{row["ChromosomeB"]}:{row["PosB"]}]' elif row["Strand1"] == "-" and row["Strand2"] == "+": - df.loc[index, "ALT"] = "N]{}:{}]".format(row["ChromosomeB"], row["PosB"]) + df.loc[index, "ALT"] = f'N]{row["ChromosomeB"]}:{row["PosB"]}]' else: - df.loc[index, "ALT"] = "N[{}:{}[".format(row["ChromosomeB"], row["PosB"]) + df.loc[index, "ALT"] = f'N[{row["ChromosomeB"]}:{row["PosB"]}[' df.loc[index, "INFO"] = ( - "SVTYPE=BND;CHRA={};CHRB={};GENEA={};GENEB={};POSA={};POSB={};ORIENTATION={},{};FOUND_DB={};" - "FOUND_IN={};TOOL_HITS={};SCORE={};FRAME_STATUS={};TRANSCRIPT_ID_A={};TRANSCRIPT_ID_B={};" - "TRANSCRIPT_VERSION_A={};TRANSCRIPT_VERSION_B={};HGNC_ID_A={};HGNC_ID_B={};EXON_NUMBER_A={},EXON_NUMBER_B={};" - "ANNOTATIONS={}".format( - row["ChromosomeA"], - row["ChromosomeB"], - row["GeneA"], - row["GeneB"], - row["PosA"], - row["PosB"], - row["Strand1"], - row["Strand2"], - row["FOUND_DB"], - row["FOUND_IN"], - row["TOOLS_HITS"], - row["SCORE"], - row["PROT_FUSION_TYPE"], - row["CDS_LEFT_ID"], - row["CDS_RIGHT_ID"], - row["Left_transcript_version"], - row["Right_transcript_version"], - row["Left_hgnc_id"], - row["Right_hgnc_id"], - row["Left_exon_number"], - row["Right_exon_number"], - row["annots"], - ) + f"SVTYPE=BND;CHRA={row['ChromosomeA']};CHRB={row['ChromosomeB']};GENEA={row['GeneA']};GENEB={row['GeneB']};" + f"POSA={row['PosA']};POSB={row['PosB']};ORIENTATION={row['Strand1']},{row['Strand2']};FOUND_DB={row['FOUND_DB']};" + f"FOUND_IN={row['FOUND_IN']};TOOL_HITS={row['TOOLS_HITS']};SCORE={row['SCORE']};FRAME_STATUS={row['PROT_FUSION_TYPE']};" + f"TRANSCRIPT_ID_A={row['CDS_LEFT_ID']};TRANSCRIPT_ID_B={row['CDS_RIGHT_ID']};" + f"TRANSCRIPT_VERSION_A={row['Left_transcript_version']};TRANSCRIPT_VERSION_B={row['Right_transcript_version']};" + f"HGNC_ID_A={row['Left_hgnc_id']};HGNC_ID_B={row['Right_hgnc_id']};" + f"EXON_NUMBER_A={row['Left_exon_number']},EXON_NUMBER_B={row['Right_exon_number']};" + f"ANNOTATIONS={row['annots']}" ) - df.loc[index, "Sample"] = "./1:{}:{}:{}".format(row["JunctionReadCount"], row["SpanningFragCount"], row["FFPM"]) + df.loc[index, "Sample"] = f"./1:{row['JunctionReadCount']}:{row['SpanningFragCount']}:{row['FFPM']}" return df From 7a700f0ff6b00a82e057d32a81ea06f717a7a437 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:05:30 +0100 Subject: [PATCH 121/175] add info on GTF output file from fusioninspector --- bin/vcf_collect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index c5157512..3761a11c 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -22,7 +22,7 @@ def vcf_collect(fusioninspector_in_file: str, fusionreport_in_file: str, sample: fusionreport_in_file (str): Path to FusionReport input file. sample (str): Sample name for the header. hgnc (str): Path to HGNC file. - gtf (str): Path to GTF file. + gtf (str): Path to output GTF file from FusionInspector. out (str): Output VCF file path. Adapted from: https://github.com/J35P312/MegaFusion From 22e4502c76aa635aa142006fd9c926e7ae373d6f Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:06:59 +0100 Subject: [PATCH 122/175] black --- bin/vcf_collect.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 3761a11c..14855fd6 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -11,7 +11,9 @@ logger = logging.getLogger() -def vcf_collect(fusioninspector_in_file: str, fusionreport_in_file: str, sample: str, hgnc: str, gtf: str, out_file) -> None: +def vcf_collect( + fusioninspector_in_file: str, fusionreport_in_file: str, sample: str, hgnc: str, gtf: str, out_file +) -> None: """ Process FusionInspector and FusionReport data, merge with GTF from FusionInspector and HGNC database, From 1478c1cc226afadb0d826be725d2ef3ddc092090 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:12:49 +0100 Subject: [PATCH 123/175] make clearer that fusionreport used is a fork --- docs/output.md | 2 ++ docs/usage.md | 4 ++++ modules/local/fusionreport/detect/meta.yml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index 92923a53..584ed5b6 100644 --- a/docs/output.md +++ b/docs/output.md @@ -200,6 +200,8 @@ The FastQC plots displayed in the MultiQC report shows _untrimmed_ reads. They m ### Fusion-report +Please note that fusion-report is executed from fork https://github.com/Clinical-Genomics/fusion-report +

Output files diff --git a/docs/usage.md b/docs/usage.md index 32ed1ff6..90725471 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -94,6 +94,10 @@ process { The four `fusion-report` files: `cosmic.db`, `fusiongdb.db`, `fusiongdb2.db`, `mitelman.db` should then be copied into the HPC `/references/fusion_report_db`. +#### Note about fusioncatcher references + +The references are only built based on ensembl version 102. It is not possible currently to use any other version/source. + ## Running the pipeline ### Samplesheet input diff --git a/modules/local/fusionreport/detect/meta.yml b/modules/local/fusionreport/detect/meta.yml index 83ba76c7..ae3601dc 100644 --- a/modules/local/fusionreport/detect/meta.yml +++ b/modules/local/fusionreport/detect/meta.yml @@ -5,7 +5,7 @@ keywords: tools: - fusionreport: description: Tool for parsing outputs from fusion detection tools - homepage: https://github.com/matq007/fusion-report + homepage: https://github.com/Clinical-Genomics/fusion-report documentation: https://matq007.github.io/fusion-report/#/ doi: "10.1101/011650" licence: ["GPL v3"] From e59823c3f7b054e262df0d5bfdd4f693017aba1d Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:18:55 +0100 Subject: [PATCH 124/175] fix header typer --- bin/vcf_collect.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 14855fd6..7156a806 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -6,7 +6,8 @@ from pathlib import Path import pandas as pd import ast -from gtfparse import read_gtf +import numpy as np +import csv logger = logging.getLogger() @@ -176,16 +177,16 @@ def header_def(sample): ##INFO=\n\ ##INFO=\n\ ##INFO=\n\ -##INFO=\n\ -##INFO=\n\ -##INFO=\n\ +##INFO=\n\ +##INFO=\n\ +##INFO=\n\ ##INFO=\n\ ##INFO=\n\ ##INFO=\n\ ##INFO=\n\ ##INFO=\n\ ##INFO=\n\ -##INFO=\n\ +##INFO=\n\ ##FORMAT=\n\ ##FORMAT=\n\ ##FORMAT=\n\ @@ -194,7 +195,6 @@ def header_def(sample): sample ) - def build_fusioninspector_dataframe(file: str) -> pd.DataFrame: """ Read FusionInspector output from a CSV file, preprocess the data, and set 'FUSION' as the index. From afda8fda8c5cb7674b6f437e384ab1537552237a Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:19:02 +0100 Subject: [PATCH 125/175] mend --- conf/modules.config | 1 - modules/local/vcf_collect/main.nf | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index f49dd02c..39135aa3 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -344,6 +344,5 @@ process { withName: VCF_COLLECT { ext.when = {!params.fusioninspector_only} - ext.prefix = { "${meta.id}_fusion_data" } } } diff --git a/modules/local/vcf_collect/main.nf b/modules/local/vcf_collect/main.nf index df999204..c634a6a3 100644 --- a/modules/local/vcf_collect/main.nf +++ b/modules/local/vcf_collect/main.nf @@ -2,8 +2,10 @@ process VCF_COLLECT { tag "$meta.id" label 'process_single' - conda "bioconda::gtfparse=2.0.1" - container "quay.io/biocontainers/gtfparse:2.0.1--pyh7cba7a3_1" + conda "conda-forge::python=3.8.3" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pandas:1.5.2' : + 'quay.io/biocontainers/pandas:1.5.2' }" input: tuple val(meta), path(fusioninspector_tsv), path(fusioninspector_gtf_tsv), path(fusionreport_report) @@ -20,7 +22,7 @@ process VCF_COLLECT { script: def prefix = task.ext.prefix ?: "${meta.id}" """ - vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}.vcf + vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}_fusion_data.vcf cat <<-END_VERSIONS > versions.yml "${task.process}": From f14bd90558e09d6dcfaf6190199be5b640247789 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:22:08 +0100 Subject: [PATCH 126/175] Revert "mend" This reverts commit afda8fda8c5cb7674b6f437e384ab1537552237a. --- conf/modules.config | 1 + modules/local/vcf_collect/main.nf | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 39135aa3..f49dd02c 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -344,5 +344,6 @@ process { withName: VCF_COLLECT { ext.when = {!params.fusioninspector_only} + ext.prefix = { "${meta.id}_fusion_data" } } } diff --git a/modules/local/vcf_collect/main.nf b/modules/local/vcf_collect/main.nf index c634a6a3..df999204 100644 --- a/modules/local/vcf_collect/main.nf +++ b/modules/local/vcf_collect/main.nf @@ -2,10 +2,8 @@ process VCF_COLLECT { tag "$meta.id" label 'process_single' - conda "conda-forge::python=3.8.3" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pandas:1.5.2' : - 'quay.io/biocontainers/pandas:1.5.2' }" + conda "bioconda::gtfparse=2.0.1" + container "quay.io/biocontainers/gtfparse:2.0.1--pyh7cba7a3_1" input: tuple val(meta), path(fusioninspector_tsv), path(fusioninspector_gtf_tsv), path(fusionreport_report) @@ -22,7 +20,7 @@ process VCF_COLLECT { script: def prefix = task.ext.prefix ?: "${meta.id}" """ - vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}_fusion_data.vcf + vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}.vcf cat <<-END_VERSIONS > versions.yml "${task.process}": From 55d95d1f8c81e78bdabf7e2dfded65b95bd69b67 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:24:02 +0100 Subject: [PATCH 127/175] revert to plain pandas container --- modules/local/vcf_collect/main.nf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/local/vcf_collect/main.nf b/modules/local/vcf_collect/main.nf index df999204..c634a6a3 100644 --- a/modules/local/vcf_collect/main.nf +++ b/modules/local/vcf_collect/main.nf @@ -2,8 +2,10 @@ process VCF_COLLECT { tag "$meta.id" label 'process_single' - conda "bioconda::gtfparse=2.0.1" - container "quay.io/biocontainers/gtfparse:2.0.1--pyh7cba7a3_1" + conda "conda-forge::python=3.8.3" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pandas:1.5.2' : + 'quay.io/biocontainers/pandas:1.5.2' }" input: tuple val(meta), path(fusioninspector_tsv), path(fusioninspector_gtf_tsv), path(fusionreport_report) @@ -20,7 +22,7 @@ process VCF_COLLECT { script: def prefix = task.ext.prefix ?: "${meta.id}" """ - vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}.vcf + vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}_fusion_data.vcf cat <<-END_VERSIONS > versions.yml "${task.process}": From 6bc7351dd1bf9e43e5c9c69312594d245841c0dd Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:24:52 +0100 Subject: [PATCH 128/175] sample should just be meta.id, add fusion_data in module for output name --- conf/modules.config | 1 - 1 file changed, 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index f49dd02c..39135aa3 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -344,6 +344,5 @@ process { withName: VCF_COLLECT { ext.when = {!params.fusioninspector_only} - ext.prefix = { "${meta.id}_fusion_data" } } } From d559232b5da7a028c15b53a80e462ef352ee6dab Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:53:27 +0100 Subject: [PATCH 129/175] remove quotes and flatten the pseudo list in annots --- bin/vcf_collect.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 7156a806..d52031ef 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -159,7 +159,7 @@ def parse_args(argv=None): return parser.parse_args(argv) -def header_def(sample): +def header_def(sample: str) -> str: """ Define the header of the VCF file """ @@ -195,6 +195,14 @@ def header_def(sample): sample ) + +def convert_to_list(annots_str: str) -> list: + try: + return ast.literal_eval(annots_str) + except (SyntaxError, ValueError): + return np.nan + + def build_fusioninspector_dataframe(file: str) -> pd.DataFrame: """ Read FusionInspector output from a CSV file, preprocess the data, and set 'FUSION' as the index. @@ -205,6 +213,11 @@ def build_fusioninspector_dataframe(file: str) -> pd.DataFrame: df[["ChromosomeB", "PosB", "Strand2"]] = df["RightBreakpoint"].str.split(":", expand=True) df[["LeftGeneName", "Left_ensembl_gene_id"]] = df["LeftGene"].str.split("^", expand=True) df[["RightGeneName", "Right_ensembl_gene_id"]] = df["RightGene"].str.split("^", expand=True) + df["annots"] = ( + df["annots"] + .apply(convert_to_list) + .apply(lambda x: ",".join(map(str, x)) if isinstance(x, list) else str(x) if pd.notna(x) else "") + ) return df.set_index(["FUSION"]) @@ -328,12 +341,7 @@ def write_vcf(df_to_print: pd.DataFrame, header: str, out_file: str) -> None: "FORMAT", "Sample", ] - ].to_csv( - path_or_buf=out_file, - sep="\t", - header=None, - index=False, - ) + ].to_csv(path_or_buf=out_file, sep="\t", header=None, index=False, quoting=csv.QUOTE_NONE) with open(out_file, "r+") as f: content = f.read() From d0a4bf2202945fcef640d335d0dddc91a00e7408 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:53:50 +0100 Subject: [PATCH 130/175] back to original container --- modules/local/vcf_collect/main.nf | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/local/vcf_collect/main.nf b/modules/local/vcf_collect/main.nf index c634a6a3..5028b2c5 100644 --- a/modules/local/vcf_collect/main.nf +++ b/modules/local/vcf_collect/main.nf @@ -2,10 +2,8 @@ process VCF_COLLECT { tag "$meta.id" label 'process_single' - conda "conda-forge::python=3.8.3" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pandas:1.5.2' : - 'quay.io/biocontainers/pandas:1.5.2' }" + conda "bioconda::gtfparse=2.0.1" + container "quay.io/biocontainers/gtfparse:2.0.1--pyh7cba7a3_1" input: tuple val(meta), path(fusioninspector_tsv), path(fusioninspector_gtf_tsv), path(fusionreport_report) From b46894252fc26553a02a1bad13162a6b6113e595 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:12:36 +0100 Subject: [PATCH 131/175] revert to pandas --- modules/local/vcf_collect/main.nf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/local/vcf_collect/main.nf b/modules/local/vcf_collect/main.nf index 5028b2c5..c634a6a3 100644 --- a/modules/local/vcf_collect/main.nf +++ b/modules/local/vcf_collect/main.nf @@ -2,8 +2,10 @@ process VCF_COLLECT { tag "$meta.id" label 'process_single' - conda "bioconda::gtfparse=2.0.1" - container "quay.io/biocontainers/gtfparse:2.0.1--pyh7cba7a3_1" + conda "conda-forge::python=3.8.3" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pandas:1.5.2' : + 'quay.io/biocontainers/pandas:1.5.2' }" input: tuple val(meta), path(fusioninspector_tsv), path(fusioninspector_gtf_tsv), path(fusionreport_report) From 960db4cf090ced0022d7956dcfcf70ec3f7ded1c Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Tue, 14 Nov 2023 22:52:38 +0100 Subject: [PATCH 132/175] apply fixes --- bin/vcf_collect.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index d52031ef..8e15ccfc 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -11,9 +11,11 @@ logger = logging.getLogger() +# vcf_collect(args.fusioninspector, args.fusionreport, args.fusioninspector_gtf, args.hgnc, args.sample, args.out) + def vcf_collect( - fusioninspector_in_file: str, fusionreport_in_file: str, sample: str, hgnc: str, gtf: str, out_file + fusioninspector_in_file: str, fusionreport_in_file: str, gtf: str, hgnc: str, sample: str, out_file ) -> None: """ Process FusionInspector and FusionReport data, @@ -25,7 +27,7 @@ def vcf_collect( fusionreport_in_file (str): Path to FusionReport input file. sample (str): Sample name for the header. hgnc (str): Path to HGNC file. - gtf (str): Path to output GTF file from FusionInspector. + gtf (str): Path to output GTF file from FusionInspector in TSV format. out (str): Output VCF file path. Adapted from: https://github.com/J35P312/MegaFusion @@ -116,7 +118,7 @@ def vcf_collect( ] ].drop_duplicates() - return write_vcf(column_manipulation(all_df), header_def(sample), out) + return write_vcf(column_manipulation(all_df), header_def(sample), out_file) def parse_args(argv=None): @@ -371,7 +373,12 @@ def build_gtf_dataframe(file: str) -> pd.DataFrame: def main(argv=None): """Coordinate argument parsing and program execution.""" args = parse_args(argv) - if not args.fusioninspector.is_file() or not args.fusionreport.is_file(): + if ( + not args.fusioninspector.is_file() + or not args.fusionreport.is_file() + or not args.fusioninspector_gtf + or not args.hgnc + ): logger.error(f"The given input file {args.fusioninspector} or {args.fusionreport} was not found!") sys.exit(2) vcf_collect(args.fusioninspector, args.fusionreport, args.fusioninspector_gtf, args.hgnc, args.sample, args.out) From 852c6ac8290aa3819f51ee0e4ebcd36b465caade Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 15 Nov 2023 09:33:18 +0100 Subject: [PATCH 133/175] compress vcf output --- bin/vcf_collect.py | 5 +++-- modules/local/vcf_collect/main.nf | 4 ++-- modules/local/vcf_collect/meta.yml | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 8e15ccfc..6a97fb50 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -8,6 +8,7 @@ import ast import numpy as np import csv +import gzip logger = logging.getLogger() @@ -343,9 +344,9 @@ def write_vcf(df_to_print: pd.DataFrame, header: str, out_file: str) -> None: "FORMAT", "Sample", ] - ].to_csv(path_or_buf=out_file, sep="\t", header=None, index=False, quoting=csv.QUOTE_NONE) + ].to_csv(path_or_buf=out_file, sep="\t", header=None, index=False, quoting=csv.QUOTE_NONE, compression='gzip') - with open(out_file, "r+") as f: + with gzip.open(out_file, "r+") as f: content = f.read() f.seek(0, 0) f.write(header.rstrip("\r\n") + "\n" + content) diff --git a/modules/local/vcf_collect/main.nf b/modules/local/vcf_collect/main.nf index c634a6a3..42f94c40 100644 --- a/modules/local/vcf_collect/main.nf +++ b/modules/local/vcf_collect/main.nf @@ -14,7 +14,7 @@ process VCF_COLLECT { output: path "versions.yml" , emit: versions - tuple val(meta), path("*vcf") , emit: vcf + tuple val(meta), path("*vcf.gz") , emit: vcf when: task.ext.when == null || task.ext.when @@ -22,7 +22,7 @@ process VCF_COLLECT { script: def prefix = task.ext.prefix ?: "${meta.id}" """ - vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}_fusion_data.vcf + vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}_fusion_data.vcf.gz cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/vcf_collect/meta.yml b/modules/local/vcf_collect/meta.yml index 40bdd6c0..de4667bb 100644 --- a/modules/local/vcf_collect/meta.yml +++ b/modules/local/vcf_collect/meta.yml @@ -32,8 +32,8 @@ output: pattern: "versions.yml" - vcf: type: file - description: File containing the summary of all fusions as vcf file - pattern: "*.tsv" + description: File containing the summary of all fusions as compressed vcf file + pattern: "*.vcf.gz" authors: - "@rannick" From 9643ed80b047441eda5735a6c61857a6e5b0a12c Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 15 Nov 2023 09:33:40 +0100 Subject: [PATCH 134/175] black --- bin/vcf_collect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 6a97fb50..4d677de0 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -344,7 +344,7 @@ def write_vcf(df_to_print: pd.DataFrame, header: str, out_file: str) -> None: "FORMAT", "Sample", ] - ].to_csv(path_or_buf=out_file, sep="\t", header=None, index=False, quoting=csv.QUOTE_NONE, compression='gzip') + ].to_csv(path_or_buf=out_file, sep="\t", header=None, index=False, quoting=csv.QUOTE_NONE, compression="gzip") with gzip.open(out_file, "r+") as f: content = f.read() From bb703dd103fa91ab4e2f28b12c673d21dbbe6232 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 15 Nov 2023 09:44:42 +0100 Subject: [PATCH 135/175] modules updatge --- modules.json | 40 +- .../agat/convertspgff2tsv/environment.yml | 1 + modules/nf-core/cat/cat/environment.yml | 7 + modules/nf-core/cat/cat/main.nf | 2 +- modules/nf-core/cat/cat/meta.yml | 7 +- modules/nf-core/cat/cat/tests/main.nf.test | 153 ++++++ .../nf-core/cat/cat/tests/main.nf.test.snap | 121 +++++ .../cat/tests/nextflow_unzipped_zipped.config | 6 + .../cat/tests/nextflow_zipped_unzipped.config | 8 + modules/nf-core/cat/cat/tests/tags.yml | 2 + modules/nf-core/cat/fastq/environment.yml | 7 + modules/nf-core/cat/fastq/main.nf | 2 +- modules/nf-core/cat/fastq/meta.yml | 4 +- modules/nf-core/cat/fastq/tests/main.nf.test | 143 ++++++ .../nf-core/cat/fastq/tests/main.nf.test.snap | 78 +++ modules/nf-core/cat/fastq/tests/tags.yml | 2 + .../dumpsoftwareversions/environment.yml | 7 + .../custom/dumpsoftwareversions/main.nf | 6 +- .../custom/dumpsoftwareversions/meta.yml | 7 +- .../dumpsoftwareversions/tests/main.nf.test | 38 ++ .../tests/main.nf.test.snap | 27 + .../dumpsoftwareversions/tests/tags.yml | 2 + modules/nf-core/fastp/environment.yml | 7 + modules/nf-core/fastp/main.nf | 2 +- modules/nf-core/fastp/meta.yml | 4 +- modules/nf-core/fastp/tests/main.nf.test | 485 ++++++++++++++++++ modules/nf-core/fastp/tests/main.nf.test.snap | 52 ++ modules/nf-core/fastp/tests/nextflow.config | 6 + modules/nf-core/fastp/tests/tags.yml | 2 + modules/nf-core/fastqc/environment.yml | 7 + modules/nf-core/fastqc/main.nf | 6 +- modules/nf-core/fastqc/meta.yml | 5 + modules/nf-core/fastqc/tests/main.nf.test | 23 +- .../nf-core/fastqc/tests/main.nf.test.snap | 10 + modules/nf-core/fastqc/tests/tags.yml | 2 + .../gatk4/bedtointervallist/environment.yml | 7 + .../nf-core/gatk4/bedtointervallist/main.nf | 2 +- .../nf-core/gatk4/bedtointervallist/meta.yml | 3 + .../createsequencedictionary/environment.yml | 7 + .../gatk4/createsequencedictionary/main.nf | 2 +- .../gatk4/createsequencedictionary/meta.yml | 4 +- .../gatk4/markduplicates/environment.yml | 8 + modules/nf-core/gatk4/markduplicates/main.nf | 2 +- modules/nf-core/gatk4/markduplicates/meta.yml | 12 +- modules/nf-core/multiqc/environment.yml | 7 + modules/nf-core/multiqc/main.nf | 6 +- modules/nf-core/multiqc/meta.yml | 11 +- .../collectinsertsizemetrics/environment.yml | 7 + .../picard/collectinsertsizemetrics/main.nf | 6 +- .../picard/collectinsertsizemetrics/meta.yml | 5 +- .../picard/collectwgsmetrics/environment.yml | 8 + .../nf-core/picard/collectwgsmetrics/main.nf | 6 +- .../nf-core/picard/collectwgsmetrics/meta.yml | 5 + .../nf-core/samtools/faidx/environment.yml | 7 + modules/nf-core/samtools/faidx/main.nf | 2 +- modules/nf-core/samtools/faidx/meta.yml | 4 + .../nf-core/samtools/index/environment.yml | 7 + modules/nf-core/samtools/index/main.nf | 2 +- modules/nf-core/samtools/index/meta.yml | 4 + modules/nf-core/samtools/sort/environment.yml | 7 + modules/nf-core/samtools/sort/main.nf | 2 +- modules/nf-core/samtools/sort/meta.yml | 3 + .../nf-core/samtools/sort/tests/main.nf.test | 70 +++ .../samtools/sort/tests/main.nf.test.snap | 39 ++ .../samtools/sort/tests/nextflow.config | 7 + modules/nf-core/samtools/sort/tests/tags.yml | 3 + modules/nf-core/samtools/view/environment.yml | 7 + modules/nf-core/samtools/view/main.nf | 2 +- modules/nf-core/samtools/view/meta.yml | 5 + modules/nf-core/star/align/environment.yml | 9 + modules/nf-core/star/align/main.nf | 2 +- modules/nf-core/star/align/meta.yml | 6 +- .../star/genomegenerate/environment.yml | 9 + modules/nf-core/star/genomegenerate/main.nf | 2 +- modules/nf-core/star/genomegenerate/meta.yml | 5 +- .../nf-core/stringtie/merge/environment.yml | 7 + modules/nf-core/stringtie/merge/main.nf | 2 +- modules/nf-core/stringtie/merge/meta.yml | 3 +- .../stringtie/stringtie/environment.yml | 7 + modules/nf-core/stringtie/stringtie/main.nf | 2 +- modules/nf-core/stringtie/stringtie/meta.yml | 3 +- 81 files changed, 1531 insertions(+), 86 deletions(-) create mode 100644 modules/nf-core/cat/cat/environment.yml create mode 100644 modules/nf-core/cat/cat/tests/main.nf.test create mode 100644 modules/nf-core/cat/cat/tests/main.nf.test.snap create mode 100644 modules/nf-core/cat/cat/tests/nextflow_unzipped_zipped.config create mode 100644 modules/nf-core/cat/cat/tests/nextflow_zipped_unzipped.config create mode 100644 modules/nf-core/cat/cat/tests/tags.yml create mode 100644 modules/nf-core/cat/fastq/environment.yml create mode 100644 modules/nf-core/cat/fastq/tests/main.nf.test create mode 100644 modules/nf-core/cat/fastq/tests/main.nf.test.snap create mode 100644 modules/nf-core/cat/fastq/tests/tags.yml create mode 100644 modules/nf-core/custom/dumpsoftwareversions/environment.yml create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml create mode 100644 modules/nf-core/fastp/environment.yml create mode 100644 modules/nf-core/fastp/tests/main.nf.test create mode 100644 modules/nf-core/fastp/tests/main.nf.test.snap create mode 100644 modules/nf-core/fastp/tests/nextflow.config create mode 100644 modules/nf-core/fastp/tests/tags.yml create mode 100644 modules/nf-core/fastqc/environment.yml create mode 100644 modules/nf-core/fastqc/tests/main.nf.test.snap create mode 100644 modules/nf-core/fastqc/tests/tags.yml create mode 100644 modules/nf-core/gatk4/bedtointervallist/environment.yml create mode 100644 modules/nf-core/gatk4/createsequencedictionary/environment.yml create mode 100644 modules/nf-core/gatk4/markduplicates/environment.yml create mode 100644 modules/nf-core/multiqc/environment.yml create mode 100644 modules/nf-core/picard/collectinsertsizemetrics/environment.yml create mode 100644 modules/nf-core/picard/collectwgsmetrics/environment.yml create mode 100644 modules/nf-core/samtools/faidx/environment.yml create mode 100644 modules/nf-core/samtools/index/environment.yml create mode 100644 modules/nf-core/samtools/sort/environment.yml create mode 100644 modules/nf-core/samtools/sort/tests/main.nf.test create mode 100644 modules/nf-core/samtools/sort/tests/main.nf.test.snap create mode 100644 modules/nf-core/samtools/sort/tests/nextflow.config create mode 100644 modules/nf-core/samtools/sort/tests/tags.yml create mode 100644 modules/nf-core/samtools/view/environment.yml create mode 100644 modules/nf-core/star/align/environment.yml create mode 100644 modules/nf-core/star/genomegenerate/environment.yml create mode 100644 modules/nf-core/stringtie/merge/environment.yml create mode 100644 modules/nf-core/stringtie/stringtie/environment.yml diff --git a/modules.json b/modules.json index a6ffdfd5..724634dc 100644 --- a/modules.json +++ b/modules.json @@ -7,7 +7,7 @@ "nf-core": { "agat/convertspgff2tsv": { "branch": "master", - "git_sha": "53e6fd5d80141e00a3b70762f4361f6af1f4303b", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "arriba": { @@ -17,97 +17,97 @@ }, "cat/cat": { "branch": "master", - "git_sha": "911696ea0b62df80e900ef244d7867d177971f73", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "cat/fastq": { "branch": "master", - "git_sha": "5c460c5a4736974abde2843294f35307ee2b0e5e", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "custom/dumpsoftwareversions": { "branch": "master", - "git_sha": "05c280924b6c768d484c7c443dad5e605c4ff4b4", + "git_sha": "bba7e362e4afead70653f84d8700588ea28d0f9e", "installed_by": ["modules"] }, "fastp": { "branch": "master", - "git_sha": "d497a4868ace3302016ea8ed4b395072d5e833cd", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "fastqc": { "branch": "master", - "git_sha": "9a4517e720bc812e95b56d23d15a1653b6db4f53", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "gatk4/bedtointervallist": { "branch": "master", - "git_sha": "cf8f9ace77aac01caa5c7cb92af5bbda7adb77bd", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "gatk4/createsequencedictionary": { "branch": "master", - "git_sha": "cf8f9ace77aac01caa5c7cb92af5bbda7adb77bd", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "gatk4/markduplicates": { "branch": "master", - "git_sha": "2aa9c2981930687792ed861b0a5f9ff7bb568a7d", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "multiqc": { "branch": "master", - "git_sha": "a6e11ac655e744f7ebc724be669dd568ffdc0e80", + "git_sha": "214d575774c172062924ad3564b4f66655600730", "installed_by": ["modules"] }, "picard/collectinsertsizemetrics": { "branch": "master", - "git_sha": "240937a2a9c30298110753292be041188891f2cb", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "picard/collectwgsmetrics": { "branch": "master", - "git_sha": "735e1e04e7e01751d2d6e97055bbdb6f70683cc1", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "samtools/faidx": { "branch": "master", - "git_sha": "fd742419940e01ba1c5ecb172c3e32ec840662fe", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "samtools/index": { "branch": "master", - "git_sha": "911696ea0b62df80e900ef244d7867d177971f73", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "samtools/sort": { "branch": "master", - "git_sha": "a0f7be95788366c1923171e358da7d049eb440f9", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "samtools/view": { "branch": "master", - "git_sha": "3ffae3598260a99e8db3207dead9f73f87f90d1f", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "star/align": { "branch": "master", - "git_sha": "cc08a888069f67cab8120259bddab8032d4c0fe3", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "star/genomegenerate": { "branch": "master", - "git_sha": "cc08a888069f67cab8120259bddab8032d4c0fe3", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "stringtie/merge": { "branch": "master", - "git_sha": "911696ea0b62df80e900ef244d7867d177971f73", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "stringtie/stringtie": { "branch": "master", - "git_sha": "911696ea0b62df80e900ef244d7867d177971f73", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] } } diff --git a/modules/nf-core/agat/convertspgff2tsv/environment.yml b/modules/nf-core/agat/convertspgff2tsv/environment.yml index 9ca0ea28..b5fdf3db 100644 --- a/modules/nf-core/agat/convertspgff2tsv/environment.yml +++ b/modules/nf-core/agat/convertspgff2tsv/environment.yml @@ -1,3 +1,4 @@ +name: agat_convertspgff2tsv channels: - conda-forge - bioconda diff --git a/modules/nf-core/cat/cat/environment.yml b/modules/nf-core/cat/cat/environment.yml new file mode 100644 index 00000000..17a04ef2 --- /dev/null +++ b/modules/nf-core/cat/cat/environment.yml @@ -0,0 +1,7 @@ +name: cat_cat +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - conda-forge::pigz=2.3.4 diff --git a/modules/nf-core/cat/cat/main.nf b/modules/nf-core/cat/cat/main.nf index 9f062219..4264a92c 100644 --- a/modules/nf-core/cat/cat/main.nf +++ b/modules/nf-core/cat/cat/main.nf @@ -2,7 +2,7 @@ process CAT_CAT { tag "$meta.id" label 'process_low' - conda "conda-forge::pigz=2.3.4" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pigz:2.3.4' : 'biocontainers/pigz:2.3.4' }" diff --git a/modules/nf-core/cat/cat/meta.yml b/modules/nf-core/cat/cat/meta.yml index 8acc0bfa..00a8db0b 100644 --- a/modules/nf-core/cat/cat/meta.yml +++ b/modules/nf-core/cat/cat/meta.yml @@ -7,9 +7,7 @@ keywords: tools: - cat: description: Just concatenation - documentation: https://man7.org/linux/man-pages/man1/cat.1.html - licence: ["GPL-3.0-or-later"] input: - meta: @@ -21,7 +19,6 @@ input: type: file description: List of compressed / uncompressed files pattern: "*" - output: - versions: type: file @@ -31,7 +28,9 @@ output: type: file description: Concatenated file. Will be gzipped if file_out ends with ".gz" pattern: "${file_out}" - authors: - "@erikrikarddaniel" - "@FriederikeHanssen" +maintainers: + - "@erikrikarddaniel" + - "@FriederikeHanssen" diff --git a/modules/nf-core/cat/cat/tests/main.nf.test b/modules/nf-core/cat/cat/tests/main.nf.test new file mode 100644 index 00000000..5766daaf --- /dev/null +++ b/modules/nf-core/cat/cat/tests/main.nf.test @@ -0,0 +1,153 @@ +nextflow_process { + + name "Test Process CAT_CAT" + script "../main.nf" + process "CAT_CAT" + tag "modules" + tag "modules_nfcore" + tag "cat" + tag "cat/cat" + + test("test_cat_unzipped_unzipped") { + when { + params { + outdir = "${outputDir}" + } + process { + """ + input[0] = + [ + [ id:'test', single_end:true ], + [ + file(params.test_data['sarscov2']['genome']['genome_fasta'], checkIfExists: true), + file(params.test_data['sarscov2']['genome']['genome_sizes'], checkIfExists: true) + ] + ] + """ + } + } + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + + test("test_cat_zipped_zipped") { + when { + params { + outdir = "${outputDir}" + } + process { + """ + input[0] = + [ + [ id:'test', single_end:true ], + [ + file(params.test_data['sarscov2']['genome']['genome_gff3_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['genome']['contigs_genome_maf_gz'], checkIfExists: true) + ] + ] + """ + } + } + then { + def lines = path(process.out.file_out.get(0).get(1)).linesGzip + assertAll( + { assert process.success }, + { assert snapshot(lines[0..5]).match("test_cat_zipped_zipped_lines") }, + { assert snapshot(lines.size()).match("test_cat_zipped_zipped_size")} + ) + } + } + + test("test_cat_zipped_unzipped") { + config './nextflow_zipped_unzipped.config' + + when { + params { + outdir = "${outputDir}" + } + process { + """ + input[0] = + [ + [ id:'test', single_end:true ], + [ + file(params.test_data['sarscov2']['genome']['genome_gff3_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['genome']['contigs_genome_maf_gz'], checkIfExists: true) + ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + + test("test_cat_unzipped_zipped") { + config './nextflow_unzipped_zipped.config' + when { + params { + outdir = "${outputDir}" + } + process { + """ + input[0] = + [ + [ id:'test', single_end:true ], + [ + file(params.test_data['sarscov2']['genome']['genome_fasta'], checkIfExists: true), + file(params.test_data['sarscov2']['genome']['genome_sizes'], checkIfExists: true) + ] + ] + """ + } + } + then { + def lines = path(process.out.file_out.get(0).get(1)).linesGzip + assertAll( + { assert process.success }, + { assert snapshot(lines[0..5]).match("test_cat_unzipped_zipped_lines") }, + { assert snapshot(lines.size()).match("test_cat_unzipped_zipped_size")} + ) + } + } + + test("test_cat_one_file_unzipped_zipped") { + config './nextflow_unzipped_zipped.config' + when { + params { + outdir = "${outputDir}" + } + process { + """ + input[0] = + [ + [ id:'test', single_end:true ], + [ + file(params.test_data['sarscov2']['genome']['genome_fasta'], checkIfExists: true) + ] + ] + """ + } + } + then { + def lines = path(process.out.file_out.get(0).get(1)).linesGzip + assertAll( + { assert process.success }, + { assert snapshot(lines[0..5]).match("test_cat_one_file_unzipped_zipped_lines") }, + { assert snapshot(lines.size()).match("test_cat_one_file_unzipped_zipped_size")} + ) + } + } +} + diff --git a/modules/nf-core/cat/cat/tests/main.nf.test.snap b/modules/nf-core/cat/cat/tests/main.nf.test.snap new file mode 100644 index 00000000..423571ba --- /dev/null +++ b/modules/nf-core/cat/cat/tests/main.nf.test.snap @@ -0,0 +1,121 @@ +{ + "test_cat_unzipped_zipped_size": { + "content": [ + 375 + ], + "timestamp": "2023-10-16T14:33:08.049445686" + }, + "test_cat_unzipped_unzipped": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.fasta:md5,f44b33a0e441ad58b2d3700270e2dbe2" + ] + ], + "1": [ + "versions.yml:md5,115ed6177ebcff24eb99d503fa5ef894" + ], + "file_out": [ + [ + { + "id": "test", + "single_end": true + }, + "test.fasta:md5,f44b33a0e441ad58b2d3700270e2dbe2" + ] + ], + "versions": [ + "versions.yml:md5,115ed6177ebcff24eb99d503fa5ef894" + ] + } + ], + "timestamp": "2023-10-16T14:32:18.500464399" + }, + "test_cat_zipped_unzipped": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "cat.txt:md5,c439d3b60e7bc03e8802a451a0d9a5d9" + ] + ], + "1": [ + "versions.yml:md5,115ed6177ebcff24eb99d503fa5ef894" + ], + "file_out": [ + [ + { + "id": "test", + "single_end": true + }, + "cat.txt:md5,c439d3b60e7bc03e8802a451a0d9a5d9" + ] + ], + "versions": [ + "versions.yml:md5,115ed6177ebcff24eb99d503fa5ef894" + ] + } + ], + "timestamp": "2023-10-16T14:32:49.642741302" + }, + "test_cat_zipped_zipped_lines": { + "content": [ + [ + "MT192765.1\tGenbank\ttranscript\t259\t29667\t.\t+\t.\tID=unknown_transcript_1;geneID=orf1ab;gene_name=orf1ab", + "MT192765.1\tGenbank\tgene\t259\t21548\t.\t+\t.\tParent=unknown_transcript_1", + "MT192765.1\tGenbank\tCDS\t259\t13461\t.\t+\t0\tParent=unknown_transcript_1;exception=\"ribosomal slippage\";gbkey=CDS;gene=orf1ab;note=\"pp1ab;translated=by -1 ribosomal frameshift\";product=\"orf1ab polyprotein\";protein_id=QIK50426.1", + "MT192765.1\tGenbank\tCDS\t13461\t21548\t.\t+\t0\tParent=unknown_transcript_1;exception=\"ribosomal slippage\";gbkey=CDS;gene=orf1ab;note=\"pp1ab;translated=by -1 ribosomal frameshift\";product=\"orf1ab polyprotein\";protein_id=QIK50426.1", + "MT192765.1\tGenbank\tCDS\t21556\t25377\t.\t+\t0\tParent=unknown_transcript_1;gbkey=CDS;gene=S;note=\"structural protein\";product=\"surface glycoprotein\";protein_id=QIK50427.1", + "MT192765.1\tGenbank\tgene\t21556\t25377\t.\t+\t.\tParent=unknown_transcript_1" + ] + ], + "timestamp": "2023-10-16T14:32:33.629048645" + }, + "test_cat_unzipped_zipped_lines": { + "content": [ + [ + ">MT192765.1 Severe acute respiratory syndrome coronavirus 2 isolate SARS-CoV-2/human/USA/PC00101P/2020, complete genome", + "GTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGT", + "GTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAG", + "TAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTTGTCCGG", + "GTGTGACCGAAAGGTAAGATGGAGAGCCTTGTCCCTGGTTTCAACGAGAAAACACACGTCCAACTCAGTTTGCCTGTTTT", + "ACAGGTTCGCGACGTGCTCGTACGTGGCTTTGGAGACTCCGTGGAGGAGGTCTTATCAGAGGCACGTCAACATCTTAAAG" + ] + ], + "timestamp": "2023-10-16T14:33:08.038830506" + }, + "test_cat_one_file_unzipped_zipped_lines": { + "content": [ + [ + ">MT192765.1 Severe acute respiratory syndrome coronavirus 2 isolate SARS-CoV-2/human/USA/PC00101P/2020, complete genome", + "GTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGT", + "GTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAG", + "TAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTTGTCCGG", + "GTGTGACCGAAAGGTAAGATGGAGAGCCTTGTCCCTGGTTTCAACGAGAAAACACACGTCCAACTCAGTTTGCCTGTTTT", + "ACAGGTTCGCGACGTGCTCGTACGTGGCTTTGGAGACTCCGTGGAGGAGGTCTTATCAGAGGCACGTCAACATCTTAAAG" + ] + ], + "timestamp": "2023-10-16T14:33:21.39642399" + }, + "test_cat_zipped_zipped_size": { + "content": [ + 78 + ], + "timestamp": "2023-10-16T14:32:33.641869244" + }, + "test_cat_one_file_unzipped_zipped_size": { + "content": [ + 374 + ], + "timestamp": "2023-10-16T14:33:21.4094373" + } +} \ No newline at end of file diff --git a/modules/nf-core/cat/cat/tests/nextflow_unzipped_zipped.config b/modules/nf-core/cat/cat/tests/nextflow_unzipped_zipped.config new file mode 100644 index 00000000..ec26b0fd --- /dev/null +++ b/modules/nf-core/cat/cat/tests/nextflow_unzipped_zipped.config @@ -0,0 +1,6 @@ + +process { + withName: CAT_CAT { + ext.prefix = 'cat.txt.gz' + } +} diff --git a/modules/nf-core/cat/cat/tests/nextflow_zipped_unzipped.config b/modules/nf-core/cat/cat/tests/nextflow_zipped_unzipped.config new file mode 100644 index 00000000..fbc79783 --- /dev/null +++ b/modules/nf-core/cat/cat/tests/nextflow_zipped_unzipped.config @@ -0,0 +1,8 @@ + +process { + + withName: CAT_CAT { + ext.prefix = 'cat.txt' + } + +} diff --git a/modules/nf-core/cat/cat/tests/tags.yml b/modules/nf-core/cat/cat/tests/tags.yml new file mode 100644 index 00000000..37b578f5 --- /dev/null +++ b/modules/nf-core/cat/cat/tests/tags.yml @@ -0,0 +1,2 @@ +cat/cat: + - modules/nf-core/cat/cat/** diff --git a/modules/nf-core/cat/fastq/environment.yml b/modules/nf-core/cat/fastq/environment.yml new file mode 100644 index 00000000..bff93add --- /dev/null +++ b/modules/nf-core/cat/fastq/environment.yml @@ -0,0 +1,7 @@ +name: cat_fastq +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - conda-forge::sed=4.7 diff --git a/modules/nf-core/cat/fastq/main.nf b/modules/nf-core/cat/fastq/main.nf index 5021e6fc..3d963784 100644 --- a/modules/nf-core/cat/fastq/main.nf +++ b/modules/nf-core/cat/fastq/main.nf @@ -2,7 +2,7 @@ process CAT_FASTQ { tag "$meta.id" label 'process_single' - conda "conda-forge::sed=4.7" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : 'nf-core/ubuntu:20.04' }" diff --git a/modules/nf-core/cat/fastq/meta.yml b/modules/nf-core/cat/fastq/meta.yml index 8a39e309..db4ac3c7 100644 --- a/modules/nf-core/cat/fastq/meta.yml +++ b/modules/nf-core/cat/fastq/meta.yml @@ -34,7 +34,9 @@ output: type: file description: File containing software versions pattern: "versions.yml" - authors: - "@joseespinosa" - "@drpatelh" +maintainers: + - "@joseespinosa" + - "@drpatelh" diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test b/modules/nf-core/cat/fastq/tests/main.nf.test new file mode 100644 index 00000000..f5f94182 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/main.nf.test @@ -0,0 +1,143 @@ +nextflow_process { + + name "Test Process CAT_FASTQ" + script "../main.nf" + process "CAT_FASTQ" + tag "modules" + tag "modules_nfcore" + tag "cat" + tag "cat/fastq" + + test("test_cat_fastq_single_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test2_1_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_paired_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test2_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test2_2_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_single_end_same_name") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_paired_end_same_name") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_single_end_single_file") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true)] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } +} diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test.snap b/modules/nf-core/cat/fastq/tests/main.nf.test.snap new file mode 100644 index 00000000..ec2342e5 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/main.nf.test.snap @@ -0,0 +1,78 @@ +{ + "test_cat_fastq_single_end": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,f9cf5e375f7de81a406144a2c70cc64d" + ] + ] + ], + "timestamp": "2023-10-17T23:19:12.990284837" + }, + "test_cat_fastq_single_end_same_name": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,63f817db7a29a03eb538104495556f66" + ] + ] + ], + "timestamp": "2023-10-17T23:19:31.554568147" + }, + "test_cat_fastq_single_end_single_file": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,e325ef7deb4023447a1f074e285761af" + ] + ] + ], + "timestamp": "2023-10-17T23:19:49.629360033" + }, + "test_cat_fastq_paired_end_same_name": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,63f817db7a29a03eb538104495556f66", + "test_2.merged.fastq.gz:md5,fe9f266f43a6fc3dcab690a18419a56e" + ] + ] + ] + ], + "timestamp": "2023-10-17T23:19:40.711617539" + }, + "test_cat_fastq_paired_end": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,f9cf5e375f7de81a406144a2c70cc64d", + "test_2.merged.fastq.gz:md5,77c8e966e130d8c6b6ec9be52fcb2bda" + ] + ] + ] + ], + "timestamp": "2023-10-18T07:53:20.923560211" + } +} \ No newline at end of file diff --git a/modules/nf-core/cat/fastq/tests/tags.yml b/modules/nf-core/cat/fastq/tests/tags.yml new file mode 100644 index 00000000..6ac43614 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/tags.yml @@ -0,0 +1,2 @@ +cat/fastq: + - modules/nf-core/cat/fastq/** diff --git a/modules/nf-core/custom/dumpsoftwareversions/environment.yml b/modules/nf-core/custom/dumpsoftwareversions/environment.yml new file mode 100644 index 00000000..f0c63f69 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/environment.yml @@ -0,0 +1,7 @@ +name: custom_dumpsoftwareversions +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::multiqc=1.17 diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf index c9d014b1..7685b33c 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/main.nf +++ b/modules/nf-core/custom/dumpsoftwareversions/main.nf @@ -2,10 +2,10 @@ process CUSTOM_DUMPSOFTWAREVERSIONS { label 'process_single' // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container - conda "bioconda::multiqc=1.15" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.15--pyhdfd78af_0' : - 'biocontainers/multiqc:1.15--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.17--pyhdfd78af_0' : + 'biocontainers/multiqc:1.17--pyhdfd78af_0' }" input: path versions diff --git a/modules/nf-core/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/custom/dumpsoftwareversions/meta.yml index c32657de..5f15a5fd 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/meta.yml +++ b/modules/nf-core/custom/dumpsoftwareversions/meta.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json name: custom_dumpsoftwareversions description: Custom module used to dump software versions within the nf-core pipeline template keywords: @@ -16,7 +16,6 @@ input: type: file description: YML file containing software versions pattern: "*.yml" - output: - yml: type: file @@ -30,7 +29,9 @@ output: type: file description: File containing software versions pattern: "versions.yml" - authors: - "@drpatelh" - "@grst" +maintainers: + - "@drpatelh" + - "@grst" diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test new file mode 100644 index 00000000..eec1db10 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test @@ -0,0 +1,38 @@ +nextflow_process { + + name "Test Process CUSTOM_DUMPSOFTWAREVERSIONS" + script "../main.nf" + process "CUSTOM_DUMPSOFTWAREVERSIONS" + tag "modules" + tag "modules_nfcore" + tag "custom" + tag "dumpsoftwareversions" + tag "custom/dumpsoftwareversions" + + test("Should run without failures") { + when { + process { + """ + def tool1_version = ''' + TOOL1: + tool1: 0.11.9 + '''.stripIndent() + + def tool2_version = ''' + TOOL2: + tool2: 1.9 + '''.stripIndent() + + input[0] = Channel.of(tool1_version, tool2_version).collectFile() + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap new file mode 100644 index 00000000..4274ed57 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap @@ -0,0 +1,27 @@ +{ + "Should run without failures": { + "content": [ + { + "0": [ + "software_versions.yml:md5,1c851188476409cda5752ce971b20b58" + ], + "1": [ + "software_versions_mqc.yml:md5,2570f4ba271ad08357b0d3d32a9cf84d" + ], + "2": [ + "versions.yml:md5,3843ac526e762117eedf8825b40683df" + ], + "mqc_yml": [ + "software_versions_mqc.yml:md5,2570f4ba271ad08357b0d3d32a9cf84d" + ], + "versions": [ + "versions.yml:md5,3843ac526e762117eedf8825b40683df" + ], + "yml": [ + "software_versions.yml:md5,1c851188476409cda5752ce971b20b58" + ] + } + ], + "timestamp": "2023-11-03T14:43:22.157011" + } +} diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml new file mode 100644 index 00000000..405aa24a --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml @@ -0,0 +1,2 @@ +custom/dumpsoftwareversions: + - modules/nf-core/custom/dumpsoftwareversions/** diff --git a/modules/nf-core/fastp/environment.yml b/modules/nf-core/fastp/environment.yml new file mode 100644 index 00000000..70389e66 --- /dev/null +++ b/modules/nf-core/fastp/environment.yml @@ -0,0 +1,7 @@ +name: fastp +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::fastp=0.23.4 diff --git a/modules/nf-core/fastp/main.nf b/modules/nf-core/fastp/main.nf index 831b7f12..c8e815ae 100644 --- a/modules/nf-core/fastp/main.nf +++ b/modules/nf-core/fastp/main.nf @@ -2,7 +2,7 @@ process FASTP { tag "$meta.id" label 'process_medium' - conda "bioconda::fastp=0.23.4" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/fastp:0.23.4--h5f740d0_0' : 'biocontainers/fastp:0.23.4--h5f740d0_0' }" diff --git a/modules/nf-core/fastp/meta.yml b/modules/nf-core/fastp/meta.yml index 197ea7ca..c22a16ab 100644 --- a/modules/nf-core/fastp/meta.yml +++ b/modules/nf-core/fastp/meta.yml @@ -33,7 +33,6 @@ input: - save_merged: type: boolean description: Specify true to save all merged reads to the a file ending in `*.merged.fastq.gz` - output: - meta: type: map @@ -71,3 +70,6 @@ output: authors: - "@drpatelh" - "@kevinmenden" +maintainers: + - "@drpatelh" + - "@kevinmenden" diff --git a/modules/nf-core/fastp/tests/main.nf.test b/modules/nf-core/fastp/tests/main.nf.test new file mode 100644 index 00000000..f610b735 --- /dev/null +++ b/modules/nf-core/fastp/tests/main.nf.test @@ -0,0 +1,485 @@ +nextflow_process { + + name "Test Process FASTP" + script "../main.nf" + process "FASTP" + tag "modules" + tag "modules_nfcore" + tag "fastp" + + test("test_fastp_single_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + adapter_fasta = [] + save_trimmed_fail = false + save_merged = false + + input[0] = [ + [ id:'test', single_end:true ], + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true) ] + ] + + input[1] = adapter_fasta + input[2] = save_trimmed_fail + input[3] = save_merged + """ + } + } + + then { + def html_text = [ "Q20 bases:12.922000 K (92.984097%)", + "single end (151 cycles)" ] + def log_text = [ "Q20 bases: 12922(92.9841%)", + "reads passed filter: 99" ] + def read_lines = ["@ERR5069949.2151832 NS500628:121:HK3MMAFX2:2:21208:10793:15304/1", + "TCATAAACCAAAGCACTCACAGTGTCAACAATTTCAGCAGGACAACGCCGACAAGTTCCGAGGAACATGTCTGGACCTATAGTTTTCATAAGTCTACACACTGAATTGAAATATTCTGGTTCTAGTGTGCCCTTAGTTAGCAATGTGCGT", + "AAAAAAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEE + { assert path(process.out.reads.get(0).get(1)).linesGzip.contains(read_line) } + } + }, + { html_text.each { html_part -> + { assert path(process.out.html.get(0).get(1)).getText().contains(html_part) } + } + }, + { assert snapshot(process.out.json).match("test_fastp_single_end_json") }, + { log_text.each { log_part -> + { assert path(process.out.log.get(0).get(1)).getText().contains(log_part) } + } + }, + { assert snapshot(process.out.versions).match("versions") } + ) + } + } + + test("test_fastp_paired_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + adapter_fasta = [] + save_trimmed_fail = false + save_merged = false + + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true) ] + ] + + input[1] = adapter_fasta + input[2] = save_trimmed_fail + input[3] = save_merged + """ + } + } + + then { + def html_text = [ "Q20 bases:25.719000 K (93.033098%)", + "The input has little adapter percentage (~0.000000%), probably it's trimmed before."] + def log_text = [ "No adapter detected for read1", + "Q30 bases: 12281(88.3716%)"] + def json_text = ['"passed_filter_reads": 198'] + def read1_lines = ["@ERR5069949.2151832 NS500628:121:HK3MMAFX2:2:21208:10793:15304/1", + "TCATAAACCAAAGCACTCACAGTGTCAACAATTTCAGCAGGACAACGCCGACAAGTTCCGAGGAACATGTCTGGACCTATAGTTTTCATAAGTCTACACACTGAATTGAAATATTCTGGTTCTAGTGTGCCCTTAGTTAGCAATGTGCGT", + "AAAAAAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEE + { assert path(process.out.reads.get(0).get(1).get(0)).linesGzip.contains(read1_line) } + } + }, + { read2_lines.each { read2_line -> + { assert path(process.out.reads.get(0).get(1).get(1)).linesGzip.contains(read2_line) } + } + }, + { html_text.each { html_part -> + { assert path(process.out.html.get(0).get(1)).getText().contains(html_part) } + } + }, + { json_text.each { json_part -> + { assert path(process.out.json.get(0).get(1)).getText().contains(json_part) } + } + }, + { log_text.each { log_part -> + { assert path(process.out.log.get(0).get(1)).getText().contains(log_part) } + } + }, + { assert snapshot(process.out.versions).match("versions") } + ) + } + } + + test("fastp test_fastp_interleaved") { + config './nextflow.config' + when { + params { + outdir = "$outputDir" + } + process { + """ + adapter_fasta = [] + save_trimmed_fail = false + save_merged = false + + input[0] = [ [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_interleaved_fastq_gz'], checkIfExists: true) ] + ] + + input[1] = adapter_fasta + input[2] = save_trimmed_fail + input[3] = save_merged + """ + } + } + + then { + def html_text = [ "Q20 bases:25.719000 K (93.033098%)", + "paired end (151 cycles + 151 cycles)"] + def log_text = [ "Q20 bases: 12922(92.9841%)", + "reads passed filter: 198"] + def read_lines = [ "@ERR5069949.2151832 NS500628:121:HK3MMAFX2:2:21208:10793:15304/1", + "TCATAAACCAAAGCACTCACAGTGTCAACAATTTCAGCAGGACAACGCCGACAAGTTCCGAGGAACATGTCTGGACCTATAGTTTTCATAAGTCTACACACTGAATTGAAATATTCTGGTTCTAGTGTGCCCTTAGTTAGCAATGTGCGT", + "AAAAAAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEE + { assert path(process.out.reads.get(0).get(1)).linesGzip.contains(read_line) } + } + }, + { html_text.each { html_part -> + { assert path(process.out.html.get(0).get(1)).getText().contains(html_part) } + } + }, + { assert snapshot(process.out.json).match("fastp test_fastp_interleaved_json") }, + { log_text.each { log_part -> + { assert path(process.out.log.get(0).get(1)).getText().contains(log_part) } + } + }, + { assert snapshot(process.out.versions).match("versions") } + ) + } + } + + test("test_fastp_single_end_trim_fail") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + adapter_fasta = [] + save_trimmed_fail = true + save_merged = false + + input[0] = [ [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true) ] + ] + input[1] = adapter_fasta + input[2] = save_trimmed_fail + input[3] = save_merged + """ + } + } + + then { + def html_text = [ "Q20 bases:12.922000 K (92.984097%)", + "single end (151 cycles)"] + def log_text = [ "Q20 bases: 12922(92.9841%)", + "reads passed filter: 99" ] + def read_lines = [ "@ERR5069949.2151832 NS500628:121:HK3MMAFX2:2:21208:10793:15304/1", + "TCATAAACCAAAGCACTCACAGTGTCAACAATTTCAGCAGGACAACGCCGACAAGTTCCGAGGAACATGTCTGGACCTATAGTTTTCATAAGTCTACACACTGAATTGAAATATTCTGGTTCTAGTGTGCCCTTAGTTAGCAATGTGCGT", + "AAAAAAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEE + { assert path(process.out.reads.get(0).get(1)).linesGzip.contains(read_line) } + } + }, + { failed_read_lines.each { failed_read_line -> + { assert path(process.out.reads_fail.get(0).get(1)).linesGzip.contains(failed_read_line) } + } + }, + { html_text.each { html_part -> + { assert path(process.out.html.get(0).get(1)).getText().contains(html_part) } + } + }, + { assert snapshot(process.out.json).match("test_fastp_single_end_trim_fail_json") }, + { log_text.each { log_part -> + { assert path(process.out.log.get(0).get(1)).getText().contains(log_part) } + } + }, + { assert snapshot(process.out.versions).match("versions") } + ) + } + } + + test("test_fastp_paired_end_trim_fail") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + adapter_fasta = [] + save_trimmed_fail = true + save_merged = false + + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ + file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true) + ] + ] + input[1] = adapter_fasta + input[2] = save_trimmed_fail + input[3] = save_merged + """ + } + } + + then { + def html_text = [ "Q20 bases:25.719000 K (93.033098%)", + "The input has little adapter percentage (~0.000000%), probably it's trimmed before."] + def log_text = [ "No adapter detected for read1", + "Q30 bases: 12281(88.3716%)"] + def json_text = ['"passed_filter_reads": 198'] + def read1_lines = ["@ERR5069949.2151832 NS500628:121:HK3MMAFX2:2:21208:10793:15304/1", + "TCATAAACCAAAGCACTCACAGTGTCAACAATTTCAGCAGGACAACGCCGACAAGTTCCGAGGAACATGTCTGGACCTATAGTTTTCATAAGTCTACACACTGAATTGAAATATTCTGGTTCTAGTGTGCCCTTAGTTAGCAATGTGCGT", + "AAAAAAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAAEEEEE + { assert path(process.out.reads.get(0).get(1).get(0)).linesGzip.contains(read1_line) } + } + }, + { read2_lines.each { read2_line -> + { assert path(process.out.reads.get(0).get(1).get(1)).linesGzip.contains(read2_line) } + } + }, + { failed_read2_lines.each { failed_read2_line -> + { assert path(process.out.reads_fail.get(0).get(1).get(1)).linesGzip.contains(failed_read2_line) } + } + }, + { html_text.each { html_part -> + { assert path(process.out.html.get(0).get(1)).getText().contains(html_part) } + } + }, + { json_text.each { json_part -> + { assert path(process.out.json.get(0).get(1)).getText().contains(json_part) } + } + }, + { log_text.each { log_part -> + { assert path(process.out.log.get(0).get(1)).getText().contains(log_part) } + } + }, + { assert snapshot(process.out.versions).match("versions") } + ) + } + } + + test("test_fastp_paired_end_merged") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + adapter_fasta = [] + save_trimmed_fail = false + save_merged = true + + input[0] = [ [ id:'test', single_end:false ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true) ] + ] + input[1] = adapter_fasta + input[2] = save_trimmed_fail + input[3] = save_merged + """ + } + } + + then { + def html_text = [ "
"] + def log_text = [ "Merged and filtered:", + "total reads: 75", + "total bases: 13683"] + def json_text = ['"merged_and_filtered": {', '"total_reads": 75', '"total_bases": 13683'] + def read1_lines = [ "@ERR5069949.1066259 NS500628:121:HK3MMAFX2:1:11312:18369:8333/1", + "CCTTATGACAGCAAGAACTGTGTATGATGATGGTGCTAGGAGAGTGTGGACACTTATGAATGTCTTGACACTCGTTTATAAAGTTTATTATGGTAATGCTTTAGATCAAGCCATTTCCATGTGGGCTCTTATAATCTCTGTTACTTC", + "AAAAAEAEEAEEEEEEEEEEEEEEEEAEEEEAEEEEEEEEAEEEEEEEEEEEEEEEEE/EAEEEEEE/6EEEEEEEEEEAEEAEEE/EE/AEEAEEEEEAEEEA/EEAAEAE + { assert path(process.out.reads.get(0).get(1).get(0)).linesGzip.contains(read1_line) } + } + }, + { read2_lines.each { read2_line -> + { assert path(process.out.reads.get(0).get(1).get(1)).linesGzip.contains(read2_line) } + } + }, + { read_merged_lines.each { read_merged_line -> + { assert path(process.out.reads_merged.get(0).get(1)).linesGzip.contains(read_merged_line) } + } + }, + { html_text.each { html_part -> + { assert path(process.out.html.get(0).get(1)).getText().contains(html_part) } + } + }, + { json_text.each { json_part -> + { assert path(process.out.json.get(0).get(1)).getText().contains(json_part) } + } + }, + { log_text.each { log_part -> + { assert path(process.out.log.get(0).get(1)).getText().contains(log_part) } + } + }, + { assert snapshot(process.out.versions).match("versions") } + ) + } + } + + test("test_fastp_paired_end_merged_adapterlist") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + adapter_fasta = file("https://github.com/nf-core/test-datasets/raw/modules/data/delete_me/fastp/adapters.fasta", checkIfExists: true) + save_trimmed_fail = false + save_merged = true + + input[0] = [ [ id:'test', single_end:false ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true) ] + ] + input[1] = adapter_fasta + input[2] = save_trimmed_fail + input[3] = save_merged + """ + } + } + + then { + def html_text = [ "
"] + def log_text = [ "Merged and filtered:", + "total reads: 75", + "total bases: 13683"] + def json_text = ['"merged_and_filtered": {', '"total_reads": 75', '"total_bases": 13683',"--adapter_fasta"] + def read1_lines = ["@ERR5069949.1066259 NS500628:121:HK3MMAFX2:1:11312:18369:8333/1", + "CCTTATGACAGCAAGAACTGTGTATGATGATGGTGCTAGGAGAGTGTGGACACTTATGAATGTCTTGACACTCGTTTATAAAGTTTATTATGGTAATGCTTTAGATCAAGCCATTTCCATGTGGGCTCTTATAATCTCTGTTACTTC", + "AAAAAEAEEAEEEEEEEEEEEEEEEEAEEEEAEEEEEEEEAEEEEEEEEEEEEEEEEE/EAEEEEEE/6EEEEEEEEEEAEEAEEE/EE/AEEAEEEEEAEEEA/EEAAEAE + { assert path(process.out.reads.get(0).get(1).get(0)).linesGzip.contains(read1_line) } + } + }, + { read2_lines.each { read2_line -> + { assert path(process.out.reads.get(0).get(1).get(1)).linesGzip.contains(read2_line) } + } + }, + { read_merged_lines.each { read_merged_line -> + { assert path(process.out.reads_merged.get(0).get(1)).linesGzip.contains(read_merged_line) } + } + }, + { html_text.each { html_part -> + { assert path(process.out.html.get(0).get(1)).getText().contains(html_part) } + } + }, + { json_text.each { json_part -> + { assert path(process.out.json.get(0).get(1)).getText().contains(json_part) } + } + }, + { log_text.each { log_part -> + { assert path(process.out.log.get(0).get(1)).getText().contains(log_part) } + } + }, + { assert snapshot(process.out.versions).match("versions") } + ) + } + } +} diff --git a/modules/nf-core/fastp/tests/main.nf.test.snap b/modules/nf-core/fastp/tests/main.nf.test.snap new file mode 100644 index 00000000..0fa68c7d --- /dev/null +++ b/modules/nf-core/fastp/tests/main.nf.test.snap @@ -0,0 +1,52 @@ +{ + "fastp test_fastp_interleaved_json": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.fastp.json:md5,168f516f7bd4b7b6c32da7cba87299a4" + ] + ] + ], + "timestamp": "2023-10-17T11:04:45.794175881" + }, + "test_fastp_single_end_json": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.fastp.json:md5,c852d7a6dba5819e4ac8d9673bedcacc" + ] + ] + ], + "timestamp": "2023-10-17T11:04:10.566343705" + }, + "versions": { + "content": [ + [ + "versions.yml:md5,48ffc994212fb1fc9f83a74fa69c9f02" + ] + ], + "timestamp": "2023-10-17T11:04:10.582076024" + }, + "test_fastp_single_end_trim_fail_json": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.fastp.json:md5,9a7ee180f000e8d00c7fb67f06293eb5" + ] + ] + ], + "timestamp": "2023-10-17T11:05:00.379878948" + } +} \ No newline at end of file diff --git a/modules/nf-core/fastp/tests/nextflow.config b/modules/nf-core/fastp/tests/nextflow.config new file mode 100644 index 00000000..0f7849ad --- /dev/null +++ b/modules/nf-core/fastp/tests/nextflow.config @@ -0,0 +1,6 @@ +process { + + withName: FASTP { + ext.args = "--interleaved_in" + } +} diff --git a/modules/nf-core/fastp/tests/tags.yml b/modules/nf-core/fastp/tests/tags.yml new file mode 100644 index 00000000..c1afcce7 --- /dev/null +++ b/modules/nf-core/fastp/tests/tags.yml @@ -0,0 +1,2 @@ +fastp: + - modules/nf-core/fastp/** diff --git a/modules/nf-core/fastqc/environment.yml b/modules/nf-core/fastqc/environment.yml new file mode 100644 index 00000000..1787b38a --- /dev/null +++ b/modules/nf-core/fastqc/environment.yml @@ -0,0 +1,7 @@ +name: fastqc +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::fastqc=0.12.1 diff --git a/modules/nf-core/fastqc/main.nf b/modules/nf-core/fastqc/main.nf index 249f9064..50e59f2b 100644 --- a/modules/nf-core/fastqc/main.nf +++ b/modules/nf-core/fastqc/main.nf @@ -2,10 +2,10 @@ process FASTQC { tag "$meta.id" label 'process_medium' - conda "bioconda::fastqc=0.11.9" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/fastqc:0.11.9--0' : - 'biocontainers/fastqc:0.11.9--0' }" + 'https://depot.galaxyproject.org/singularity/fastqc:0.12.1--hdfd78af_0' : + 'biocontainers/fastqc:0.12.1--hdfd78af_0' }" input: tuple val(meta), path(reads) diff --git a/modules/nf-core/fastqc/meta.yml b/modules/nf-core/fastqc/meta.yml index 4da5bb5a..ee5507e0 100644 --- a/modules/nf-core/fastqc/meta.yml +++ b/modules/nf-core/fastqc/meta.yml @@ -50,3 +50,8 @@ authors: - "@grst" - "@ewels" - "@FelixKrueger" +maintainers: + - "@drpatelh" + - "@grst" + - "@ewels" + - "@FelixKrueger" diff --git a/modules/nf-core/fastqc/tests/main.nf.test b/modules/nf-core/fastqc/tests/main.nf.test index 3961de60..6437a144 100644 --- a/modules/nf-core/fastqc/tests/main.nf.test +++ b/modules/nf-core/fastqc/tests/main.nf.test @@ -1,13 +1,18 @@ nextflow_process { name "Test Process FASTQC" - script "modules/nf-core/fastqc/main.nf" + script "../main.nf" process "FASTQC" + tag "modules" + tag "modules_nfcore" tag "fastqc" test("Single-Read") { when { + params { + outdir = "$outputDir" + } process { """ input[0] = [ @@ -21,12 +26,16 @@ nextflow_process { } then { - assert process.success - assert process.out.html.get(0).get(1) ==~ ".*/test_fastqc.html" - assert path(process.out.html.get(0).get(1)).getText().contains("File typeConventional base calls") - assert process.out.zip.get(0).get(1) ==~ ".*/test_fastqc.zip" + assertAll ( + { assert process.success }, + // NOTE The report contains the date inside it, which means that the md5sum is stable per day, but not longer than that. So you can't md5sum it. + // looks like this:
Mon 2 Oct 2023
test.gz
+ // https://github.com/nf-core/modules/pull/3903#issuecomment-1743620039 + { assert process.out.html.get(0).get(1) ==~ ".*/test_fastqc.html" }, + { assert path(process.out.html.get(0).get(1)).getText().contains("File typeConventional base calls") }, + { assert snapshot(process.out.versions).match("versions") }, + { assert process.out.zip.get(0).get(1) ==~ ".*/test_fastqc.zip" } + ) } - } - } diff --git a/modules/nf-core/fastqc/tests/main.nf.test.snap b/modules/nf-core/fastqc/tests/main.nf.test.snap new file mode 100644 index 00000000..636a32ce --- /dev/null +++ b/modules/nf-core/fastqc/tests/main.nf.test.snap @@ -0,0 +1,10 @@ +{ + "versions": { + "content": [ + [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ] + ], + "timestamp": "2023-10-09T23:40:54+0000" + } +} \ No newline at end of file diff --git a/modules/nf-core/fastqc/tests/tags.yml b/modules/nf-core/fastqc/tests/tags.yml new file mode 100644 index 00000000..7834294b --- /dev/null +++ b/modules/nf-core/fastqc/tests/tags.yml @@ -0,0 +1,2 @@ +fastqc: + - modules/nf-core/fastqc/** diff --git a/modules/nf-core/gatk4/bedtointervallist/environment.yml b/modules/nf-core/gatk4/bedtointervallist/environment.yml new file mode 100644 index 00000000..e7cb4280 --- /dev/null +++ b/modules/nf-core/gatk4/bedtointervallist/environment.yml @@ -0,0 +1,7 @@ +name: gatk4_bedtointervallist +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::gatk4=4.4.0.0 diff --git a/modules/nf-core/gatk4/bedtointervallist/main.nf b/modules/nf-core/gatk4/bedtointervallist/main.nf index 24968c38..88b24b1a 100644 --- a/modules/nf-core/gatk4/bedtointervallist/main.nf +++ b/modules/nf-core/gatk4/bedtointervallist/main.nf @@ -2,7 +2,7 @@ process GATK4_BEDTOINTERVALLIST { tag "$meta.id" label 'process_medium' - conda "bioconda::gatk4=4.4.0.0" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/gatk4:4.4.0.0--py36hdfd78af_0': 'biocontainers/gatk4:4.4.0.0--py36hdfd78af_0' }" diff --git a/modules/nf-core/gatk4/bedtointervallist/meta.yml b/modules/nf-core/gatk4/bedtointervallist/meta.yml index 83617a7c..187da885 100644 --- a/modules/nf-core/gatk4/bedtointervallist/meta.yml +++ b/modules/nf-core/gatk4/bedtointervallist/meta.yml @@ -46,3 +46,6 @@ output: authors: - "@kevinmenden" - "@ramprasadn" +maintainers: + - "@kevinmenden" + - "@ramprasadn" diff --git a/modules/nf-core/gatk4/createsequencedictionary/environment.yml b/modules/nf-core/gatk4/createsequencedictionary/environment.yml new file mode 100644 index 00000000..db663e14 --- /dev/null +++ b/modules/nf-core/gatk4/createsequencedictionary/environment.yml @@ -0,0 +1,7 @@ +name: gatk4_createsequencedictionary +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::gatk4=4.4.0.0 diff --git a/modules/nf-core/gatk4/createsequencedictionary/main.nf b/modules/nf-core/gatk4/createsequencedictionary/main.nf index 3e4efdd9..b47ad162 100644 --- a/modules/nf-core/gatk4/createsequencedictionary/main.nf +++ b/modules/nf-core/gatk4/createsequencedictionary/main.nf @@ -2,7 +2,7 @@ process GATK4_CREATESEQUENCEDICTIONARY { tag "$fasta" label 'process_medium' - conda "bioconda::gatk4=4.4.0.0" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/gatk4:4.4.0.0--py36hdfd78af_0': 'biocontainers/gatk4:4.4.0.0--py36hdfd78af_0' }" diff --git a/modules/nf-core/gatk4/createsequencedictionary/meta.yml b/modules/nf-core/gatk4/createsequencedictionary/meta.yml index 9b8b8c89..f9d70be0 100644 --- a/modules/nf-core/gatk4/createsequencedictionary/meta.yml +++ b/modules/nf-core/gatk4/createsequencedictionary/meta.yml @@ -15,7 +15,6 @@ tools: documentation: https://gatk.broadinstitute.org/hc/en-us/categories/360002369672s doi: 10.1158/1538-7445.AM2017-3590 licence: ["Apache-2.0"] - input: - meta: type: map @@ -38,3 +37,6 @@ output: authors: - "@maxulysse" - "@ramprasadn" +maintainers: + - "@maxulysse" + - "@ramprasadn" diff --git a/modules/nf-core/gatk4/markduplicates/environment.yml b/modules/nf-core/gatk4/markduplicates/environment.yml new file mode 100644 index 00000000..9adad104 --- /dev/null +++ b/modules/nf-core/gatk4/markduplicates/environment.yml @@ -0,0 +1,8 @@ +name: gatk4_markduplicates +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::gatk4=4.4.0.0 + - bioconda::samtools=1.17 diff --git a/modules/nf-core/gatk4/markduplicates/main.nf b/modules/nf-core/gatk4/markduplicates/main.nf index 59e52a3d..564b86d3 100644 --- a/modules/nf-core/gatk4/markduplicates/main.nf +++ b/modules/nf-core/gatk4/markduplicates/main.nf @@ -2,7 +2,7 @@ process GATK4_MARKDUPLICATES { tag "$meta.id" label 'process_medium' - conda "bioconda::gatk4=4.4.0.0 bioconda::samtools=1.17" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/mulled-v2-d9e7bad0f7fbc8f4458d5c3ab7ffaaf0235b59fb:f857e2d6cc88d35580d01cf39e0959a68b83c1d9-0': 'biocontainers/mulled-v2-d9e7bad0f7fbc8f4458d5c3ab7ffaaf0235b59fb:f857e2d6cc88d35580d01cf39e0959a68b83c1d9-0' }" diff --git a/modules/nf-core/gatk4/markduplicates/meta.yml b/modules/nf-core/gatk4/markduplicates/meta.yml index d3e75505..b0f09d4b 100644 --- a/modules/nf-core/gatk4/markduplicates/meta.yml +++ b/modules/nf-core/gatk4/markduplicates/meta.yml @@ -7,16 +7,12 @@ keywords: - sort tools: - gatk4: - description: - Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools - with a primary focus on variant discovery and genotyping. Its powerful processing engine - and high-performance computing features make it capable of taking on projects of any size. + description: Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools with a primary focus on variant discovery and genotyping. Its powerful processing engine and high-performance computing features make it capable of taking on projects of any size. homepage: https://gatk.broadinstitute.org/hc/en-us documentation: https://gatk.broadinstitute.org/hc/en-us/articles/360037052812-MarkDuplicates-Picard- tool_dev_url: https://github.com/broadinstitute/gatk doi: 10.1158/1538-7445.AM2017-3590 licence: ["MIT"] - input: - meta: type: map @@ -35,7 +31,6 @@ input: type: file description: Fasta index file pattern: "*.{fai}" - output: - meta: type: map @@ -66,8 +61,11 @@ output: type: file description: Duplicate metrics file generated by GATK pattern: "*.{metrics.txt}" - authors: - "@ajodeh-juma" - "@FriederikeHanssen" - "@maxulysse" +maintainers: + - "@ajodeh-juma" + - "@FriederikeHanssen" + - "@maxulysse" diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/multiqc/environment.yml new file mode 100644 index 00000000..d2a9f21a --- /dev/null +++ b/modules/nf-core/multiqc/environment.yml @@ -0,0 +1,7 @@ +name: multiqc +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::multiqc=1.17 diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index 65d7dd0d..2bbc3983 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -1,10 +1,10 @@ process MULTIQC { label 'process_single' - conda "bioconda::multiqc=1.15" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.15--pyhdfd78af_0' : - 'biocontainers/multiqc:1.15--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.17--pyhdfd78af_0' : + 'biocontainers/multiqc:1.17--pyhdfd78af_0' }" input: path multiqc_files, stageAs: "?/*" diff --git a/modules/nf-core/multiqc/meta.yml b/modules/nf-core/multiqc/meta.yml index f93b5ee5..f1aa660e 100644 --- a/modules/nf-core/multiqc/meta.yml +++ b/modules/nf-core/multiqc/meta.yml @@ -1,5 +1,5 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json -name: MultiQC +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json +name: multiqc description: Aggregate results from bioinformatics analyses across many samples into a single report keywords: - QC @@ -13,7 +13,6 @@ tools: homepage: https://multiqc.info/ documentation: https://multiqc.info/docs/ licence: ["GPL-3.0-or-later"] - input: - multiqc_files: type: file @@ -31,7 +30,6 @@ input: type: file description: Optional logo file for MultiQC pattern: "*.{png}" - output: - report: type: file @@ -54,3 +52,8 @@ authors: - "@bunop" - "@drpatelh" - "@jfy133" +maintainers: + - "@abhi18av" + - "@bunop" + - "@drpatelh" + - "@jfy133" diff --git a/modules/nf-core/picard/collectinsertsizemetrics/environment.yml b/modules/nf-core/picard/collectinsertsizemetrics/environment.yml new file mode 100644 index 00000000..5c85f872 --- /dev/null +++ b/modules/nf-core/picard/collectinsertsizemetrics/environment.yml @@ -0,0 +1,7 @@ +name: picard_collectinsertsizemetrics +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::picard=3.1.0 diff --git a/modules/nf-core/picard/collectinsertsizemetrics/main.nf b/modules/nf-core/picard/collectinsertsizemetrics/main.nf index 1d538fae..48e4d2ad 100644 --- a/modules/nf-core/picard/collectinsertsizemetrics/main.nf +++ b/modules/nf-core/picard/collectinsertsizemetrics/main.nf @@ -2,10 +2,10 @@ process PICARD_COLLECTINSERTSIZEMETRICS { tag "$meta.id" label 'process_single' - conda "bioconda::picard=3.0.0" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/picard:3.0.0--hdfd78af_1' : - 'biocontainers/picard:3.0.0--hdfd78af_1' }" + 'https://depot.galaxyproject.org/singularity/picard:3.1.0--hdfd78af_0' : + 'biocontainers/picard:3.1.0--hdfd78af_0' }" input: tuple val(meta), path(bam) diff --git a/modules/nf-core/picard/collectinsertsizemetrics/meta.yml b/modules/nf-core/picard/collectinsertsizemetrics/meta.yml index e611bdd4..efd5abe0 100644 --- a/modules/nf-core/picard/collectinsertsizemetrics/meta.yml +++ b/modules/nf-core/picard/collectinsertsizemetrics/meta.yml @@ -6,7 +6,6 @@ keywords: - insert - statistics - bam - tools: - "picard": description: "Java tools for working with NGS data in the BAM format" @@ -14,7 +13,6 @@ tools: documentation: "https://broadinstitute.github.io/picard/" tool_dev_url: "https://github.com/broadinstitute/picard" licence: "['MIT']" - input: - meta: type: map @@ -25,7 +23,6 @@ input: type: file description: BAM/CRAM/SAM file pattern: "*.{bam,cram,sam}" - output: - meta: type: map @@ -46,3 +43,5 @@ output: pattern: "*.txt" authors: - "@FerriolCalvet" +maintainers: + - "@FerriolCalvet" diff --git a/modules/nf-core/picard/collectwgsmetrics/environment.yml b/modules/nf-core/picard/collectwgsmetrics/environment.yml new file mode 100644 index 00000000..8adda491 --- /dev/null +++ b/modules/nf-core/picard/collectwgsmetrics/environment.yml @@ -0,0 +1,8 @@ +name: picard_collectwgsmetrics +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::picard=3.1.0 + - r::r-base diff --git a/modules/nf-core/picard/collectwgsmetrics/main.nf b/modules/nf-core/picard/collectwgsmetrics/main.nf index 1d59334c..67aa5b5e 100644 --- a/modules/nf-core/picard/collectwgsmetrics/main.nf +++ b/modules/nf-core/picard/collectwgsmetrics/main.nf @@ -2,10 +2,10 @@ process PICARD_COLLECTWGSMETRICS { tag "$meta.id" label 'process_single' - conda "bioconda::picard=3.0.0 r::r-base" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/picard:3.0.0--hdfd78af_1' : - 'biocontainers/picard:3.0.0--hdfd78af_1' }" + 'https://depot.galaxyproject.org/singularity/picard:3.1.0--hdfd78af_0' : + 'biocontainers/picard:3.1.0--hdfd78af_0' }" input: tuple val(meta), path(bam), path(bai) diff --git a/modules/nf-core/picard/collectwgsmetrics/meta.yml b/modules/nf-core/picard/collectwgsmetrics/meta.yml index 19906f08..5576ef92 100644 --- a/modules/nf-core/picard/collectwgsmetrics/meta.yml +++ b/modules/nf-core/picard/collectwgsmetrics/meta.yml @@ -68,3 +68,8 @@ authors: - "@flowuenne" - "@lassefolkersen" - "@ramprasadn" +maintainers: + - "@drpatelh" + - "@flowuenne" + - "@lassefolkersen" + - "@ramprasadn" diff --git a/modules/nf-core/samtools/faidx/environment.yml b/modules/nf-core/samtools/faidx/environment.yml new file mode 100644 index 00000000..73badedb --- /dev/null +++ b/modules/nf-core/samtools/faidx/environment.yml @@ -0,0 +1,7 @@ +name: samtools_faidx +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::samtools=1.17 diff --git a/modules/nf-core/samtools/faidx/main.nf b/modules/nf-core/samtools/faidx/main.nf index 59ed3088..3aa98822 100644 --- a/modules/nf-core/samtools/faidx/main.nf +++ b/modules/nf-core/samtools/faidx/main.nf @@ -2,7 +2,7 @@ process SAMTOOLS_FAIDX { tag "$fasta" label 'process_single' - conda "bioconda::samtools=1.17" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/samtools:1.17--h00cdaf9_0' : 'biocontainers/samtools:1.17--h00cdaf9_0' }" diff --git a/modules/nf-core/samtools/faidx/meta.yml b/modules/nf-core/samtools/faidx/meta.yml index 957b25e5..e189af28 100644 --- a/modules/nf-core/samtools/faidx/meta.yml +++ b/modules/nf-core/samtools/faidx/meta.yml @@ -55,3 +55,7 @@ authors: - "@drpatelh" - "@ewels" - "@phue" +maintainers: + - "@drpatelh" + - "@ewels" + - "@phue" diff --git a/modules/nf-core/samtools/index/environment.yml b/modules/nf-core/samtools/index/environment.yml new file mode 100644 index 00000000..3c6f95b2 --- /dev/null +++ b/modules/nf-core/samtools/index/environment.yml @@ -0,0 +1,7 @@ +name: samtools_index +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::samtools=1.17 diff --git a/modules/nf-core/samtools/index/main.nf b/modules/nf-core/samtools/index/main.nf index 0b20aa4b..256bd7c4 100644 --- a/modules/nf-core/samtools/index/main.nf +++ b/modules/nf-core/samtools/index/main.nf @@ -2,7 +2,7 @@ process SAMTOOLS_INDEX { tag "$meta.id" label 'process_low' - conda "bioconda::samtools=1.17" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/samtools:1.17--h00cdaf9_0' : 'biocontainers/samtools:1.17--h00cdaf9_0' }" diff --git a/modules/nf-core/samtools/index/meta.yml b/modules/nf-core/samtools/index/meta.yml index 8bd2fa6f..01a4ee03 100644 --- a/modules/nf-core/samtools/index/meta.yml +++ b/modules/nf-core/samtools/index/meta.yml @@ -51,3 +51,7 @@ authors: - "@drpatelh" - "@ewels" - "@maxulysse" +maintainers: + - "@drpatelh" + - "@ewels" + - "@maxulysse" diff --git a/modules/nf-core/samtools/sort/environment.yml b/modules/nf-core/samtools/sort/environment.yml new file mode 100644 index 00000000..508659f0 --- /dev/null +++ b/modules/nf-core/samtools/sort/environment.yml @@ -0,0 +1,7 @@ +name: samtools_sort +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::samtools=1.17 diff --git a/modules/nf-core/samtools/sort/main.nf b/modules/nf-core/samtools/sort/main.nf index 2b7753fd..60f0c634 100644 --- a/modules/nf-core/samtools/sort/main.nf +++ b/modules/nf-core/samtools/sort/main.nf @@ -2,7 +2,7 @@ process SAMTOOLS_SORT { tag "$meta.id" label 'process_medium' - conda "bioconda::samtools=1.17" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/samtools:1.17--h00cdaf9_0' : 'biocontainers/samtools:1.17--h00cdaf9_0' }" diff --git a/modules/nf-core/samtools/sort/meta.yml b/modules/nf-core/samtools/sort/meta.yml index 07328431..2200de72 100644 --- a/modules/nf-core/samtools/sort/meta.yml +++ b/modules/nf-core/samtools/sort/meta.yml @@ -46,3 +46,6 @@ output: authors: - "@drpatelh" - "@ewels" +maintainers: + - "@drpatelh" + - "@ewels" diff --git a/modules/nf-core/samtools/sort/tests/main.nf.test b/modules/nf-core/samtools/sort/tests/main.nf.test new file mode 100644 index 00000000..1f72f3b9 --- /dev/null +++ b/modules/nf-core/samtools/sort/tests/main.nf.test @@ -0,0 +1,70 @@ +nextflow_process { + + name "Test Process SAMTOOLS_SORT" + script "../main.nf" + process "SAMTOOLS_SORT" + tag "modules" + tag "modules_nfcore" + tag "samtools" + tag "samtools/sort" + + test("test_samtools_sort") { + + config "./nextflow.config" + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:false ], + [ + file(params.test_data['sarscov2']['illumina']['test_paired_end_bam'], checkIfExists: true) + ] + ] + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + + test("test_samtools_sort_stub") { + + config "./nextflow.config" + options "-stub-run" + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:false ], + [ + file(params.test_data['sarscov2']['illumina']['test_paired_end_bam'], checkIfExists: true) + ] + ] + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + +} diff --git a/modules/nf-core/samtools/sort/tests/main.nf.test.snap b/modules/nf-core/samtools/sort/tests/main.nf.test.snap new file mode 100644 index 00000000..a43566da --- /dev/null +++ b/modules/nf-core/samtools/sort/tests/main.nf.test.snap @@ -0,0 +1,39 @@ +{ + "test_samtools_sort": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam:md5,a29570e7607d217c2fa4d75829e09cd7" + ] + ], + "1": [ + + ], + "2": [ + "versions.yml:md5,46f7a36082fa1f68285fe30d689244e8" + ], + "bam": [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam:md5,a29570e7607d217c2fa4d75829e09cd7" + ] + ], + "csi": [ + + ], + "versions": [ + "versions.yml:md5,46f7a36082fa1f68285fe30d689244e8" + ] + } + ], + "timestamp": "2023-10-17T17:21:46.5427968" + } +} \ No newline at end of file diff --git a/modules/nf-core/samtools/sort/tests/nextflow.config b/modules/nf-core/samtools/sort/tests/nextflow.config new file mode 100644 index 00000000..d0f35086 --- /dev/null +++ b/modules/nf-core/samtools/sort/tests/nextflow.config @@ -0,0 +1,7 @@ +process { + + withName: SAMTOOLS_SORT { + ext.prefix = { "${meta.id}.sorted" } + } + +} diff --git a/modules/nf-core/samtools/sort/tests/tags.yml b/modules/nf-core/samtools/sort/tests/tags.yml new file mode 100644 index 00000000..cd63ea20 --- /dev/null +++ b/modules/nf-core/samtools/sort/tests/tags.yml @@ -0,0 +1,3 @@ +samtools/sort: + - modules/nf-core/samtools/sort/** + - tests/modules/nf-core/samtools/sort/** diff --git a/modules/nf-core/samtools/view/environment.yml b/modules/nf-core/samtools/view/environment.yml new file mode 100644 index 00000000..141e7bd8 --- /dev/null +++ b/modules/nf-core/samtools/view/environment.yml @@ -0,0 +1,7 @@ +name: samtools_view +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::samtools=1.17 diff --git a/modules/nf-core/samtools/view/main.nf b/modules/nf-core/samtools/view/main.nf index cb91facf..ddf3f88a 100644 --- a/modules/nf-core/samtools/view/main.nf +++ b/modules/nf-core/samtools/view/main.nf @@ -2,7 +2,7 @@ process SAMTOOLS_VIEW { tag "$meta.id" label 'process_low' - conda "bioconda::samtools=1.17" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/samtools:1.17--h00cdaf9_0' : 'biocontainers/samtools:1.17--h00cdaf9_0' }" diff --git a/modules/nf-core/samtools/view/meta.yml b/modules/nf-core/samtools/view/meta.yml index 3b05450b..3dadafae 100644 --- a/modules/nf-core/samtools/view/meta.yml +++ b/modules/nf-core/samtools/view/meta.yml @@ -82,3 +82,8 @@ authors: - "@joseespinosa" - "@FriederikeHanssen" - "@priyanka-surana" +maintainers: + - "@drpatelh" + - "@joseespinosa" + - "@FriederikeHanssen" + - "@priyanka-surana" diff --git a/modules/nf-core/star/align/environment.yml b/modules/nf-core/star/align/environment.yml new file mode 100644 index 00000000..6db20988 --- /dev/null +++ b/modules/nf-core/star/align/environment.yml @@ -0,0 +1,9 @@ +name: star_align +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::star=2.7.10a + - bioconda::samtools=1.16.1 + - conda-forge::gawk=5.1.0 diff --git a/modules/nf-core/star/align/main.nf b/modules/nf-core/star/align/main.nf index d0e20384..fa645a6d 100644 --- a/modules/nf-core/star/align/main.nf +++ b/modules/nf-core/star/align/main.nf @@ -2,7 +2,7 @@ process STAR_ALIGN { tag "$meta.id" label 'process_high' - conda "bioconda::star=2.7.10a bioconda::samtools=1.16.1 conda-forge::gawk=5.1.0" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:1df389393721fc66f3fd8778ad938ac711951107-0' : 'biocontainers/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:1df389393721fc66f3fd8778ad938ac711951107-0' }" diff --git a/modules/nf-core/star/align/meta.yml b/modules/nf-core/star/align/meta.yml index 3d8fed0c..e80dbb7d 100644 --- a/modules/nf-core/star/align/meta.yml +++ b/modules/nf-core/star/align/meta.yml @@ -52,7 +52,6 @@ input: - seq_center: type: string description: Sequencing center - output: - bam: type: file @@ -106,8 +105,11 @@ output: type: file description: STAR output bedGraph format file(s) (optional) pattern: "*.bg" - authors: - "@kevinmenden" - "@drpatelh" - "@praveenraj2018" +maintainers: + - "@kevinmenden" + - "@drpatelh" + - "@praveenraj2018" diff --git a/modules/nf-core/star/genomegenerate/environment.yml b/modules/nf-core/star/genomegenerate/environment.yml new file mode 100644 index 00000000..0b35ff51 --- /dev/null +++ b/modules/nf-core/star/genomegenerate/environment.yml @@ -0,0 +1,9 @@ +name: star_genomegenerate +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::star=2.7.10a + - bioconda::samtools=1.16.1 + - conda-forge::gawk=5.1.0 diff --git a/modules/nf-core/star/genomegenerate/main.nf b/modules/nf-core/star/genomegenerate/main.nf index 43424042..473e62a6 100644 --- a/modules/nf-core/star/genomegenerate/main.nf +++ b/modules/nf-core/star/genomegenerate/main.nf @@ -2,7 +2,7 @@ process STAR_GENOMEGENERATE { tag "$fasta" label 'process_high' - conda "bioconda::star=2.7.10a bioconda::samtools=1.16.1 conda-forge::gawk=5.1.0" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:1df389393721fc66f3fd8778ad938ac711951107-0' : 'biocontainers/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:1df389393721fc66f3fd8778ad938ac711951107-0' }" diff --git a/modules/nf-core/star/genomegenerate/meta.yml b/modules/nf-core/star/genomegenerate/meta.yml index eba2d9cf..1061e1b8 100644 --- a/modules/nf-core/star/genomegenerate/meta.yml +++ b/modules/nf-core/star/genomegenerate/meta.yml @@ -31,7 +31,6 @@ input: - gtf: type: file description: GTF file of the reference genome - output: - meta: type: map @@ -46,7 +45,9 @@ output: type: file description: File containing software versions pattern: "versions.yml" - authors: - "@kevinmenden" - "@drpatelh" +maintainers: + - "@kevinmenden" + - "@drpatelh" diff --git a/modules/nf-core/stringtie/merge/environment.yml b/modules/nf-core/stringtie/merge/environment.yml new file mode 100644 index 00000000..9914b202 --- /dev/null +++ b/modules/nf-core/stringtie/merge/environment.yml @@ -0,0 +1,7 @@ +name: stringtie_merge +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::stringtie=2.2.1 diff --git a/modules/nf-core/stringtie/merge/main.nf b/modules/nf-core/stringtie/merge/main.nf index 12224f78..c2568219 100644 --- a/modules/nf-core/stringtie/merge/main.nf +++ b/modules/nf-core/stringtie/merge/main.nf @@ -2,7 +2,7 @@ process STRINGTIE_MERGE { label 'process_medium' // Note: 2.7X indices incompatible with AWS iGenomes. - conda "bioconda::stringtie=2.2.1" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/stringtie:2.2.1--hecb563c_2' : 'biocontainers/stringtie:2.2.1--hecb563c_2' }" diff --git a/modules/nf-core/stringtie/merge/meta.yml b/modules/nf-core/stringtie/merge/meta.yml index 2e9784fe..5d02d678 100644 --- a/modules/nf-core/stringtie/merge/meta.yml +++ b/modules/nf-core/stringtie/merge/meta.yml @@ -32,6 +32,7 @@ output: type: file description: File containing software versions pattern: "versions.yml" - authors: - "@yuukiiwa" +maintainers: + - "@yuukiiwa" diff --git a/modules/nf-core/stringtie/stringtie/environment.yml b/modules/nf-core/stringtie/stringtie/environment.yml new file mode 100644 index 00000000..7a0eccdb --- /dev/null +++ b/modules/nf-core/stringtie/stringtie/environment.yml @@ -0,0 +1,7 @@ +name: stringtie_stringtie +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::stringtie=2.2.1 diff --git a/modules/nf-core/stringtie/stringtie/main.nf b/modules/nf-core/stringtie/stringtie/main.nf index d0f8b563..6e25ba27 100644 --- a/modules/nf-core/stringtie/stringtie/main.nf +++ b/modules/nf-core/stringtie/stringtie/main.nf @@ -2,7 +2,7 @@ process STRINGTIE_STRINGTIE { tag "$meta.id" label 'process_medium' - conda "bioconda::stringtie=2.2.1" + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/stringtie:2.2.1--hecb563c_2' : 'biocontainers/stringtie:2.2.1--hecb563c_2' }" diff --git a/modules/nf-core/stringtie/stringtie/meta.yml b/modules/nf-core/stringtie/stringtie/meta.yml index 75518470..d8ebdd88 100644 --- a/modules/nf-core/stringtie/stringtie/meta.yml +++ b/modules/nf-core/stringtie/stringtie/meta.yml @@ -5,7 +5,6 @@ keywords: - assembly - quantification - gtf - tools: - stringtie2: description: | @@ -55,3 +54,5 @@ output: pattern: "versions.yml" authors: - "@drpatelh" +maintainers: + - "@drpatelh" From ef9f57a869e79446397e22c260aa9c80cab962ab Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 15 Nov 2023 10:40:39 +0100 Subject: [PATCH 136/175] move compression out of vcf_collect --- bin/vcf_collect.py | 5 ++--- modules/local/vcf_collect/main.nf | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 4d677de0..8e15ccfc 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -8,7 +8,6 @@ import ast import numpy as np import csv -import gzip logger = logging.getLogger() @@ -344,9 +343,9 @@ def write_vcf(df_to_print: pd.DataFrame, header: str, out_file: str) -> None: "FORMAT", "Sample", ] - ].to_csv(path_or_buf=out_file, sep="\t", header=None, index=False, quoting=csv.QUOTE_NONE, compression="gzip") + ].to_csv(path_or_buf=out_file, sep="\t", header=None, index=False, quoting=csv.QUOTE_NONE) - with gzip.open(out_file, "r+") as f: + with open(out_file, "r+") as f: content = f.read() f.seek(0, 0) f.write(header.rstrip("\r\n") + "\n" + content) diff --git a/modules/local/vcf_collect/main.nf b/modules/local/vcf_collect/main.nf index 42f94c40..1b8e56fe 100644 --- a/modules/local/vcf_collect/main.nf +++ b/modules/local/vcf_collect/main.nf @@ -22,7 +22,8 @@ process VCF_COLLECT { script: def prefix = task.ext.prefix ?: "${meta.id}" """ - vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}_fusion_data.vcf.gz + vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}_fusion_data.vcf + gzip ${prefix}_fusion_data.vcf cat <<-END_VERSIONS > versions.yml "${task.process}": From f33a5e9f36271d07c11cb28b762a2c3859aa7302 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:25:59 +0100 Subject: [PATCH 137/175] fix ,/; --- bin/vcf_collect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 8e15ccfc..57cfbcaa 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -319,7 +319,7 @@ def column_manipulation(df: pd.DataFrame) -> pd.DataFrame: f"TRANSCRIPT_ID_A={row['CDS_LEFT_ID']};TRANSCRIPT_ID_B={row['CDS_RIGHT_ID']};" f"TRANSCRIPT_VERSION_A={row['Left_transcript_version']};TRANSCRIPT_VERSION_B={row['Right_transcript_version']};" f"HGNC_ID_A={row['Left_hgnc_id']};HGNC_ID_B={row['Right_hgnc_id']};" - f"EXON_NUMBER_A={row['Left_exon_number']},EXON_NUMBER_B={row['Right_exon_number']};" + f"EXON_NUMBER_A={row['Left_exon_number']};EXON_NUMBER_B={row['Right_exon_number']};" f"ANNOTATIONS={row['annots']}" ) df.loc[index, "Sample"] = f"./1:{row['JunctionReadCount']}:{row['SpanningFragCount']}:{row['FFPM']}" From a8b07438c70907de25960cfbf934e1b175e82cd2 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:35:59 +0100 Subject: [PATCH 138/175] fix values display vcf --- bin/vcf_collect.py | 13 ++++++++++++- modules/local/vcf_collect/main.nf | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 57cfbcaa..dbbd384e 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -275,7 +275,7 @@ def read_build_fusionreport(fusionreport_file: str) -> pd.DataFrame: concatenate_columns, axis=1 ) fusion_report.columns = fusion_report.columns.str.upper() - fusion_report["FOUND_DB"] = fusion_report["FOUND_DB"].apply(lambda x: ", ".join(x)) + fusion_report["FOUND_DB"] = fusion_report["FOUND_DB"].apply(lambda x: ",".join(x)) fusion_report[["GeneA", "GeneB"]] = fusion_report["FUSION"].str.split("--", expand=True) return fusion_report[["FUSION", "GeneA", "GeneB", "TOOLS_HITS", "SCORE", "FOUND_DB", "FOUND_IN"]].set_index( @@ -297,6 +297,17 @@ def column_manipulation(df: pd.DataFrame) -> pd.DataFrame: df["INFO"] = "" df["Sample"] = "" df["Strand1"] = df["Strand1"].astype(str) + df["JunctionReadCount"] = df["JunctionReadCount"].fillna(0).astype(int).astype(str) + df["SpanningFragCount"] = df["SpanningFragCount"].fillna(0).astype(int).astype(str) + df["FFPM"] = df["FFPM"].fillna(0).astype(float).astype(str) + df["ChromosomeA"] = df["ChromosomeA"].fillna(0).astype(int).astype(str) + df["ChromosomeB"] = df["ChromosomeB"].fillna(0).astype(int).astype(str) + df["Left_hgnc_id"] = df["Left_hgnc_id"].fillna(0).astype(int).astype(str) + df["Right_hgnc_id"] = df["Right_hgnc_id"].fillna(0).astype(int).astype(str) + df["Left_exon_number"] = df["Left_exon_number"].fillna(0).astype(int).astype(str) + df["Right_exon_number"] = df["Right_exon_number"].fillna(0).astype(int).astype(str) + df["Left_transcript_version"] = df["Left_transcript_version"].fillna(0).astype(int).astype(str) + df["Right_transcript_version"] = df["Right_transcript_version"].fillna(0).astype(int).astype(str) for index, row in df.iterrows(): if row["Strand1"] == "nan": diff --git a/modules/local/vcf_collect/main.nf b/modules/local/vcf_collect/main.nf index 1b8e56fe..2af4a777 100644 --- a/modules/local/vcf_collect/main.nf +++ b/modules/local/vcf_collect/main.nf @@ -2,7 +2,7 @@ process VCF_COLLECT { tag "$meta.id" label 'process_single' - conda "conda-forge::python=3.8.3" + conda "conda-forge::pandas=1.5.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pandas:1.5.2' : 'quay.io/biocontainers/pandas:1.5.2' }" From acf64a1d862a7c16fd68d6bd736100e1c1c4ae1c Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 17 Nov 2023 13:54:11 +0100 Subject: [PATCH 139/175] add hgnc id for fusionreport-only entries in vcf and add bug fix --- bin/vcf_collect.py | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index dbbd384e..b5ff126d 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -37,19 +37,36 @@ def vcf_collect( .join(read_build_fusionreport(fusionreport_in_file), how="outer", on="FUSION") .reset_index() ) + hgnc_df = build_hgnc_dataframe(hgnc) - df = build_hgcn_dataframe(hgnc).merge( - merged_df, how="right", left_on="ensembl_gene_id", right_on="Left_ensembl_gene_id" + df_symbol = merged_df[merged_df["Left_ensembl_gene_id"].isna()] + df_not_symbol = merged_df[merged_df["Left_ensembl_gene_id"].notna()] + + df_not_symbol = hgnc_df.merge( + df_not_symbol, how="right", left_on="ensembl_gene_id", right_on="Left_ensembl_gene_id" ) + df_symbol = hgnc_df.merge(df_symbol, how="right", left_on="symbol", right_on="GeneA") + df = pd.concat([df_not_symbol, df_symbol]) df = df.rename(columns={"hgnc_id": "Left_hgnc_id"}) - df = build_hgcn_dataframe(hgnc).merge(df, how="right", left_on="ensembl_gene_id", right_on="Right_ensembl_gene_id") + + df_symbol = df[df["Right_ensembl_gene_id"].isna()] + df_not_symbol = df[df["Right_ensembl_gene_id"].notna()] + + df_not_symbol = hgnc_df.merge( + df_not_symbol, how="right", left_on="ensembl_gene_id", right_on="Right_ensembl_gene_id" + ) + df_symbol = hgnc_df.merge(df_symbol, how="right", left_on="symbol", right_on="GeneB") + df = pd.concat([df_not_symbol, df_symbol]) df = df.rename(columns={"hgnc_id": "Right_hgnc_id"}) + gtf_df = build_gtf_dataframe(gtf) all_df = df.merge(gtf_df, how="left", left_on="CDS_LEFT_ID", right_on="Transcript_id") - all_df[["PosA", "orig_start", "orig_end"]] = all_df[["PosA", "orig_start", "orig_end"]].fillna(0) - all_df[["PosA", "orig_start", "orig_end"]] = all_df[["PosA", "orig_start", "orig_end"]].astype(int) + all_df[["PosA", "orig_start", "orig_end"]] = all_df[["PosA", "orig_start", "orig_end"]].fillna(0).astype(int) - all_df = all_df[(all_df["PosA"] >= all_df["orig_start"]) & (all_df["PosA"] <= all_df["orig_end"])] + all_df = all_df[ + ((all_df["PosA"] >= all_df["orig_start"]) & (all_df["PosA"] <= all_df["orig_end"])) + | ((all_df["orig_start"] == 0) & (all_df["orig_end"] == 0)) + ] all_df = all_df.rename(columns={"transcript_version": "Left_transcript_version"}) all_df = all_df.rename(columns={"exon_number": "Left_exon_number"}) all_df = all_df[ @@ -83,10 +100,14 @@ def vcf_collect( all_df = all_df.merge(gtf_df, how="left", left_on="CDS_RIGHT_ID", right_on="Transcript_id") all_df[["PosB", "orig_start", "orig_end"]] = all_df[["PosB", "orig_start", "orig_end"]].fillna(0) all_df[["PosB", "orig_start", "orig_end"]] = all_df[["PosB", "orig_start", "orig_end"]].astype(int) - all_df = all_df[(all_df["PosB"] >= all_df["orig_start"]) & (all_df["PosB"] <= all_df["orig_end"])] + all_df = all_df[ + ((all_df["PosB"] >= all_df["orig_start"]) & (all_df["PosB"] <= all_df["orig_end"])) + | ((all_df["orig_start"] == 0) & (all_df["orig_end"] == 0)) + ] all_df = all_df.rename(columns={"transcript_version": "Right_transcript_version"}) all_df = all_df.rename(columns={"exon_number": "Right_exon_number"}) + all_df = all_df[ [ "FUSION", @@ -300,8 +321,8 @@ def column_manipulation(df: pd.DataFrame) -> pd.DataFrame: df["JunctionReadCount"] = df["JunctionReadCount"].fillna(0).astype(int).astype(str) df["SpanningFragCount"] = df["SpanningFragCount"].fillna(0).astype(int).astype(str) df["FFPM"] = df["FFPM"].fillna(0).astype(float).astype(str) - df["ChromosomeA"] = df["ChromosomeA"].fillna(0).astype(int).astype(str) - df["ChromosomeB"] = df["ChromosomeB"].fillna(0).astype(int).astype(str) + df["ChromosomeA"] = df["ChromosomeA"].fillna(0).astype(str) + df["ChromosomeB"] = df["ChromosomeB"].fillna(0).astype(str) df["Left_hgnc_id"] = df["Left_hgnc_id"].fillna(0).astype(int).astype(str) df["Right_hgnc_id"] = df["Right_hgnc_id"].fillna(0).astype(int).astype(str) df["Left_exon_number"] = df["Left_exon_number"].fillna(0).astype(int).astype(str) @@ -334,6 +355,7 @@ def column_manipulation(df: pd.DataFrame) -> pd.DataFrame: f"ANNOTATIONS={row['annots']}" ) df.loc[index, "Sample"] = f"./1:{row['JunctionReadCount']}:{row['SpanningFragCount']}:{row['FFPM']}" + return df @@ -362,13 +384,13 @@ def write_vcf(df_to_print: pd.DataFrame, header: str, out_file: str) -> None: f.write(header.rstrip("\r\n") + "\n" + content) -def build_hgcn_dataframe(file: str) -> pd.DataFrame: +def build_hgnc_dataframe(file: str) -> pd.DataFrame: """ Build a DataFrame from HGNC input file, extracting 'hgnc_id' and 'ensembl_gene_id' columns. """ df = pd.read_csv(file, sep="\t", low_memory=False) df["hgnc_id"] = df["hgnc_id"].str.replace("HGNC:", "") - return df[["hgnc_id", "ensembl_gene_id"]].dropna() + return df[["hgnc_id", "ensembl_gene_id", "symbol"]].dropna() def build_gtf_dataframe(file: str) -> pd.DataFrame: From 1f872d4223508dd04ebb8d5585322967df04ad37 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 20 Nov 2023 09:44:01 +0100 Subject: [PATCH 140/175] add info on fusionreport scoring, genome supported, tool_cutoff and arriba visualisation --- docs/output.md | 19 +++++++++++++++++-- docs/usage.md | 8 +++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/output.md b/docs/output.md index 584ed5b6..4056c6aa 100644 --- a/docs/output.md +++ b/docs/output.md @@ -118,6 +118,8 @@ If no parameters are specified, the default is applied.
+The visualisation displays the fusions that fusioninspector outputs. That means that fusions from all callers are aggregated (by fusion-report) and then analyzed through fusioninspector (Note: Fusioninspecor contains a filtering step!). + ### Cat
@@ -216,9 +218,22 @@ Please note that fusion-report is executed from fork https://github.com/Clinical
[Fusion-report](https://github.com/matq007/fusion-report) is a tool for parsing outputs from fusion detection tools. -The score is explained [on the original fusion-report github page](https://matq007.github.io/fusion-report/#/score). +The score is explained here: . Summary: + +The weights for databases are as follows: + +* FusionGDB (20) +* COSMIC (40) +* MITELMAN (40) +* FusionGDB2 (0) + +The final formula for calculating score is: + +$$ +score = 0.5 * \sum_{tool}^{tools} f(fusion, tool)*w(tool) + 0.5 * \sum_{db}^{dbs} g(fusion, db)*w(db) +$$ -`--fusionreport_filter` can be used to filter the output of fusion-report to fusions identified by at least 2 different tools. +All tools have the same weight. ### Kallisto diff --git a/docs/usage.md b/docs/usage.md index 90725471..c4b0359d 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -184,6 +184,12 @@ outdir: './results/' You can also generate such `YAML`/`JSON` files via [nf-core/launch](https://nf-co.re/launch). + +:::warning +Conda is not currently supported. +Supported genome is currently only GRCh38. +::: + ### Options #### Trimming @@ -212,7 +218,7 @@ nextflow run nf-core/rnafusion \ --tools_cutoff ``` -`--tools_cutoff INT` will discard fusions detected by less than INT tools both for display in fusionreport html index and to consider in fusioninspector. +`--tools_cutoff INT` will discard fusions detected by less than INT tools both for display in fusionreport html index and to consider in fusioninspector. Default = 1, no filtering. #### Adding custom fusions to consider as well as the detected set: whitelist From 5fb4b5700eb71278177b5917836c24b0761ceddd Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 20 Nov 2023 09:53:16 +0100 Subject: [PATCH 141/175] improve documentation --- docs/output.md | 2 ++ docs/usage.md | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/docs/output.md b/docs/output.md index 2a55052a..1bf58249 100644 --- a/docs/output.md +++ b/docs/output.md @@ -255,6 +255,8 @@ Quantifying abundances of transcripts from bulk and single-cell RNA-Seq data, or - `vcf_collect` - `_fusion_data.vcf` - contains the fusions in vcf format with collected statistics. +Vcf-collect takes as input the results of fusion-report and fusioninspector. That means fusions from all tools are aggregated. Fusioninspector applies a filter so it is possible some fusions detected by a caller are not filtered out by fusioninspector. In those cases, vcf-collect will display the fusions, but a lot of data will be missing as fusioninspector performs the analysis for each fusion. +
[Megafusion](https://github.com/J35P312/MegaFusion) converts RNA fusion files to SV VCF and collects statistics and metrics in a VCF file. diff --git a/docs/usage.md b/docs/usage.md index 1f651fd5..c97942c9 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -299,6 +299,19 @@ Use the parameter `--cram` to compress the BAM files to CRAM for specific tools. - `--cram arriba,starfusion`, default: [] - `--cram arriba` +### Troubleshooting + +#### GstrandBit issues + +The issue below sometimes occurs: + +``` +EXITING because of FATAL ERROR: cannot insert sequence on the fly because of strand GstrandBit problem +SOLUTION: please contact STAR author at https://groups.google.com/forum/#!forum/rna-star +``` + +As the error message suggest, it is a STAR-related error and your best luck in solving it will be the forum. + ### Updating the pipeline When you run the above command, Nextflow automatically pulls the pipeline code from GitHub and stores it as a cached version. When running the pipeline after this, it will always use the cached version if available - even if the pipeline has been updated since. To make sure that you're running the latest version of the pipeline, make sure that you regularly update the cached version of the pipeline: From 5389e7692e4806ee3fd9170773de4647d40a9ed2 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 20 Nov 2023 10:42:17 +0100 Subject: [PATCH 142/175] prettier --- docs/output.md | 8 ++++---- docs/usage.md | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/output.md b/docs/output.md index 1bf58249..559e5fb2 100644 --- a/docs/output.md +++ b/docs/output.md @@ -222,10 +222,10 @@ The score is explained here: Date: Mon, 20 Nov 2023 13:20:41 +0100 Subject: [PATCH 143/175] Update docs/usage.md Co-authored-by: Peter Pruisscher <57712924+peterpru@users.noreply.github.com> --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 494ddfab..b3625332 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -309,7 +309,7 @@ EXITING because of FATAL ERROR: cannot insert sequence on the fly because of str SOLUTION: please contact STAR author at https://groups.google.com/forum/#!forum/rna-star ``` -As the error message suggest, it is a STAR-related error and your best luck in solving it will be the forum. +As the error message suggests, it is a STAR-related error and your best luck in solving it will be the forum. ### Updating the pipeline From 9d7b98a0988e32968981e212f1f233b07229bcdf Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:49:29 +0100 Subject: [PATCH 144/175] Fill non-empty values within each group for 'exon_number' and 'transcript_version' --- bin/vcf_collect.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index b5ff126d..5d359107 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -63,10 +63,24 @@ def vcf_collect( all_df = df.merge(gtf_df, how="left", left_on="CDS_LEFT_ID", right_on="Transcript_id") all_df[["PosA", "orig_start", "orig_end"]] = all_df[["PosA", "orig_start", "orig_end"]].fillna(0).astype(int) + all_df["bla"] = ((all_df["PosA"] >= all_df["orig_start"]) & (all_df["PosA"] <= all_df["orig_end"])) | ( + (all_df["orig_start"] == 0) & (all_df["orig_end"] == 0) + ) + all_df = all_df[ ((all_df["PosA"] >= all_df["orig_start"]) & (all_df["PosA"] <= all_df["orig_end"])) | ((all_df["orig_start"] == 0) & (all_df["orig_end"] == 0)) ] + + all_df.replace("", np.nan, inplace=True) + # Fill non-empty values within each group for 'exon_number' and 'transcript_version' + all_df["exon_number"] = all_df.groupby("PosA")["exon_number"].transform( + lambda x: x.fillna(method="ffill").fillna(method="bfill") + ) + all_df["transcript_version"] = all_df.groupby("PosA")["transcript_version"].transform( + lambda x: x.fillna(method="ffill").fillna(method="bfill") + ) + all_df = all_df.rename(columns={"transcript_version": "Left_transcript_version"}) all_df = all_df.rename(columns={"exon_number": "Left_exon_number"}) all_df = all_df[ From 6fe7cdd43607f71f3fe4d536086c29519478b319 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:53:32 +0100 Subject: [PATCH 145/175] remove max_sensitivity and max_mate_dist from fusioninspector filtering --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index 39135aa3..61535a7e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -94,7 +94,7 @@ process { withName: FUSIONINSPECTOR { ext.when = { !params.skip_vis } ext.args = { params.fusioninspector_limitSjdbInsertNsj != 1000000 ? "--STAR_xtra_params \"--limitSjdbInsertNsj ${params.fusioninspector_limitSjdbInsertNsj}\"" : '' } - ext.args2 = '--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect' + ext.args2 = '--annotate --examine_coding_effect' } withName: FUSIONREPORT { From 0b2e62a5c5613735d4ca0c279d34db653e2d315d Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 20 Nov 2023 17:29:47 +0100 Subject: [PATCH 146/175] add fastp output and fastqc after trim to multiqc --- assets/multiqc_config.yaml | 30 ------------------------------ assets/multiqc_config.yml | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 30 deletions(-) delete mode 100644 assets/multiqc_config.yaml diff --git a/assets/multiqc_config.yaml b/assets/multiqc_config.yaml deleted file mode 100644 index 61602fc9..00000000 --- a/assets/multiqc_config.yaml +++ /dev/null @@ -1,30 +0,0 @@ -custom_logo: "nf-core-rnafusion_logo_light.png" -custom_logo_url: https://github.com/nf-core/rnafusion -custom_logo_title: "nf-core/rnafusion" - -report_comment: > - This report has been generated by the nf-core/rnafusion - analysis pipeline. For information about how to interpret these results, please see the - documentation. -report_section_order: - "nf-core-rnafusion-methods-description": - order: -1000 - software_versions: - order: -1001 - "nf-core-rnafusion-summary": - order: -1002 - -export_plots: true - -run_modules: - - fastqc - - picard - - fastp - - gatk - - star - -module_order: - - fastqc: - name: "FastQC" - path_filters: - - "*.zip" diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 40a09e62..bce891ff 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -10,3 +10,26 @@ report_section_order: order: -1001 export_plots: true + +# Run only these modules +run_modules: + - custom_content + - fastqc + - fastp + - star + - samtools + - picard + + +module_order: + - fastqc: + name: "FastQC (raw)" + info: "This section of the report shows FastQC results before adapter trimming." + path_filters: + - "./fastqc/raw/*.zip" + - fastp + - fastqc: + name: "FastQC (trimmed)" + info: "This section of the report shows FastQC results after adapter trimming." + path_filters: + - "./fastqc/trim/*.zip" From 311e1abe8f9acf4a97d7e534f4474ec80d9eb1ff Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:47:38 +0100 Subject: [PATCH 147/175] fix multiqx --- assets/multiqc_config.yml | 11 +++++++---- conf/modules.config | 5 +++++ workflows/rnafusion.nf | 3 +++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index bce891ff..1683ee24 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -4,10 +4,12 @@ report_comment: > documentation. report_section_order: - software_versions: + nf-core-rnafusion-methods-description: order: -1000 - nf-core-rnafusion-summary: + software_versions: order: -1001 + nf-core-rnafusion-summary: + order: -1002 export_plots: true @@ -19,6 +21,7 @@ run_modules: - star - samtools - picard + - arriba module_order: @@ -26,10 +29,10 @@ module_order: name: "FastQC (raw)" info: "This section of the report shows FastQC results before adapter trimming." path_filters: - - "./fastqc/raw/*.zip" + - "./fastqc/*.zip" - fastp - fastqc: name: "FastQC (trimmed)" info: "This section of the report shows FastQC results after adapter trimming." path_filters: - - "./fastqc/trim/*.zip" + - "./fastqc_for_trim/*.zip" diff --git a/conf/modules.config b/conf/modules.config index 61535a7e..962bb1e9 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -68,6 +68,11 @@ process { withName: FASTQC { ext.args = '--quiet' ext.when = { !params.skip_qc } + publishDir = [ + path: { "${params.outdir}/fastqc" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } withName: FASTQC_FOR_TRIM { diff --git a/workflows/rnafusion.nf b/workflows/rnafusion.nf index 6792eff8..78133922 100644 --- a/workflows/rnafusion.nf +++ b/workflows/rnafusion.nf @@ -263,6 +263,9 @@ workflow RNAFUSION { ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(FASTP.out.html.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(FASTP.out.json.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(FASTQC_FOR_FASTP.out.zip.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(STARFUSION_WORKFLOW.out.star_stats.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.rnaseq_metrics.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.duplicate_metrics.collect{it[1]}.ifEmpty([])) From de83a08d9e47e4f833a58bef6db0f4983d49d22f Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:58:24 +0100 Subject: [PATCH 148/175] add files to multiqc --- assets/multiqc_config.yml | 5 +++++ subworkflows/local/fusioninspector_workflow.nf | 5 ++++- subworkflows/local/starfusion_workflow.nf | 3 +++ workflows/rnafusion.nf | 2 ++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 1683ee24..886ceef0 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -36,3 +36,8 @@ module_order: info: "This section of the report shows FastQC results after adapter trimming." path_filters: - "./fastqc_for_trim/*.zip" + - arriba: + name: "Arriba visualisation" + info: "This section of the report shows Arriba visualisation after FusionInspector analysis." + path_filters: + - "./arriba_visualisation/*.pdf" diff --git a/subworkflows/local/fusioninspector_workflow.nf b/subworkflows/local/fusioninspector_workflow.nf index 32173e81..3f1f9f13 100644 --- a/subworkflows/local/fusioninspector_workflow.nf +++ b/subworkflows/local/fusioninspector_workflow.nf @@ -19,6 +19,7 @@ workflow FUSIONINSPECTOR_WORKFLOW { main: ch_versions = Channel.empty() + ch_arriba_visualisation = Channel.empty() index ="${params.starfusion_ref}" ch_fusion_list = ( params.tools_cutoff > 1 ? fusion_list_filtered : fusion_list ) @@ -53,9 +54,11 @@ workflow FUSIONINSPECTOR_WORKFLOW { ch_bam_sorted_indexed_fusions = bam_sorted_indexed.join(FUSIONINSPECTOR.out.tsv) ARRIBA_VISUALISATION(ch_bam_sorted_indexed_fusions, ch_gtf, ch_arriba_ref_protein_domains, ch_arriba_ref_cytobands) ch_versions = ch_versions.mix(ARRIBA_VISUALISATION.out.versions) + ch_arriba_visualisation = ARRIBA_VISUALISATION.out.pdf } emit: - versions = ch_versions.ifEmpty(null) + arriba_visualisation = ch_arriba_visualisation.ifEmpty(null) + versions = ch_versions.ifEmpty(null) } diff --git a/subworkflows/local/starfusion_workflow.nf b/subworkflows/local/starfusion_workflow.nf index 2c3a118e..38264c01 100644 --- a/subworkflows/local/starfusion_workflow.nf +++ b/subworkflows/local/starfusion_workflow.nf @@ -45,16 +45,19 @@ workflow STARFUSION_WORKFLOW { ch_starfusion_fusions = STARFUSION.out.fusions ch_star_stats = STAR_FOR_STARFUSION.out.log_final + ch_star_gene_count = STAR_FOR_STARFUSION.out.read_per_gene_tab } } else { ch_starfusion_fusions = reads.combine(Channel.value(file(ch_dummy_file, checkIfExists:true))) .map { meta, reads, fusions -> [ meta, fusions ] } ch_star_stats = Channel.empty() + ch_star_gene_count = Channel.empty() } emit: fusions = ch_starfusion_fusions star_stats = ch_star_stats + star_gene_count = ch_star_gene_count ch_bam_sorted = ch_align.ifEmpty([[],[]]) ch_bam_sorted_indexed = bam_sorted_indexed.ifEmpty([[],[],[]]) versions = ch_versions.ifEmpty(null) diff --git a/workflows/rnafusion.nf b/workflows/rnafusion.nf index 78133922..cd223444 100644 --- a/workflows/rnafusion.nf +++ b/workflows/rnafusion.nf @@ -267,9 +267,11 @@ workflow RNAFUSION { ch_multiqc_files = ch_multiqc_files.mix(FASTP.out.json.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(FASTQC_FOR_FASTP.out.zip.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(STARFUSION_WORKFLOW.out.star_stats.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(STARFUSION_WORKFLOW.out.star_gene_count.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.rnaseq_metrics.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.duplicate_metrics.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.insertsize_metrics.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(FUSIONINSPECTOR_WORKFLOW.out.arriba_visualisation.collect{it[1]}.ifEmpty([])) From 45505e75a471e826e7f99bd6d190a8f8cbaebb6a Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:00:36 +0100 Subject: [PATCH 149/175] prettier --- assets/multiqc_config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 886ceef0..7e090b4a 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -23,7 +23,6 @@ run_modules: - picard - arriba - module_order: - fastqc: name: "FastQC (raw)" From 3641a361edc075669f7dfbb73b0147c183fe9263 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:08:19 +0100 Subject: [PATCH 150/175] modules update + fix channel io --- modules.json | 4 +- modules/nf-core/multiqc/environment.yml | 2 +- modules/nf-core/multiqc/main.nf | 6 +- modules/nf-core/multiqc/tests/main.nf.test | 91 +++++++++++++++++++ modules/nf-core/multiqc/tests/tags.yml | 2 + .../samtools/index/tests/csi.nextflow.config | 7 ++ .../nf-core/samtools/index/tests/main.nf.test | 87 ++++++++++++++++++ .../samtools/index/tests/main.nf.test.snap | 28 ++++++ modules/nf-core/samtools/index/tests/tags.yml | 2 + subworkflows/local/trim_workflow.nf | 9 ++ workflows/rnafusion.nf | 6 +- 11 files changed, 236 insertions(+), 8 deletions(-) create mode 100644 modules/nf-core/multiqc/tests/main.nf.test create mode 100644 modules/nf-core/multiqc/tests/tags.yml create mode 100644 modules/nf-core/samtools/index/tests/csi.nextflow.config create mode 100644 modules/nf-core/samtools/index/tests/main.nf.test create mode 100644 modules/nf-core/samtools/index/tests/main.nf.test.snap create mode 100644 modules/nf-core/samtools/index/tests/tags.yml diff --git a/modules.json b/modules.json index 724634dc..d13e5998 100644 --- a/modules.json +++ b/modules.json @@ -57,7 +57,7 @@ }, "multiqc": { "branch": "master", - "git_sha": "214d575774c172062924ad3564b4f66655600730", + "git_sha": "1537442a7be4a78efa3d1ff700a923c627bbda5d", "installed_by": ["modules"] }, "picard/collectinsertsizemetrics": { @@ -77,7 +77,7 @@ }, "samtools/index": { "branch": "master", - "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", + "git_sha": "5394565c5fe4c760e5b35977ec7607c62e81d1f8", "installed_by": ["modules"] }, "samtools/sort": { diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/multiqc/environment.yml index d2a9f21a..bc0bdb5b 100644 --- a/modules/nf-core/multiqc/environment.yml +++ b/modules/nf-core/multiqc/environment.yml @@ -4,4 +4,4 @@ channels: - bioconda - defaults dependencies: - - bioconda::multiqc=1.17 + - bioconda::multiqc=1.18 diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index 2bbc3983..00cc48d2 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -3,8 +3,8 @@ process MULTIQC { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.17--pyhdfd78af_0' : - 'biocontainers/multiqc:1.17--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.18--pyhdfd78af_0' : + 'biocontainers/multiqc:1.18--pyhdfd78af_0' }" input: path multiqc_files, stageAs: "?/*" @@ -25,12 +25,14 @@ process MULTIQC { def args = task.ext.args ?: '' def config = multiqc_config ? "--config $multiqc_config" : '' def extra_config = extra_multiqc_config ? "--config $extra_multiqc_config" : '' + def logo = multiqc_logo ? /--cl-config 'custom_logo: "${multiqc_logo}"'/ : '' """ multiqc \\ --force \\ $args \\ $config \\ $extra_config \\ + $logo \\ . cat <<-END_VERSIONS > versions.yml diff --git a/modules/nf-core/multiqc/tests/main.nf.test b/modules/nf-core/multiqc/tests/main.nf.test new file mode 100644 index 00000000..68fffa90 --- /dev/null +++ b/modules/nf-core/multiqc/tests/main.nf.test @@ -0,0 +1,91 @@ +nextflow_process { + + name "Test Process MULTIQC" + script "../main.nf" + process "MULTIQC" + tag "modules" + tag "modules_nfcore" + tag "multiqc" + + test("MULTIQC: FASTQC") { + + setup { + run("FASTQC") { + script "../../fastqc/main.nf" + process { + """ + input[0] = Channel.of([ + [ id: 'test', single_end: false ], + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true)] + ]) + """ + } + } + } + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = FASTQC.out.zip.collect { it[1] } + input[1] = [] + input[2] = [] + input[3] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert path(process.out.report.get(0)).exists() }, + { assert path(process.out.data.get(0)).exists() }, + { assert path(process.out.versions.get(0)).getText().contains("multiqc") } + ) + } + + } + + test("MULTIQC: FASTQC and a config file") { + + setup { + run("FASTQC") { + script "../../fastqc/main.nf" + process { + """ + input[0] = Channel.of([ + [ id: 'test', single_end: false ], + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true)] + ]) + """ + } + } + } + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = FASTQC.out.zip.collect { it[1] } + input[1] = Channel.of(file("https://github.com/nf-core/tools/raw/dev/nf_core/pipeline-template/assets/multiqc_config.yml", checkIfExists: true)) + input[2] = [] + input[3] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert path(process.out.report.get(0)).exists() }, + { assert path(process.out.data.get(0)).exists() }, + { assert path(process.out.versions.get(0)).getText().contains("multiqc") } + ) + } + + } +} diff --git a/modules/nf-core/multiqc/tests/tags.yml b/modules/nf-core/multiqc/tests/tags.yml new file mode 100644 index 00000000..bea6c0d3 --- /dev/null +++ b/modules/nf-core/multiqc/tests/tags.yml @@ -0,0 +1,2 @@ +multiqc: + - modules/nf-core/multiqc/** diff --git a/modules/nf-core/samtools/index/tests/csi.nextflow.config b/modules/nf-core/samtools/index/tests/csi.nextflow.config new file mode 100644 index 00000000..0ed260ef --- /dev/null +++ b/modules/nf-core/samtools/index/tests/csi.nextflow.config @@ -0,0 +1,7 @@ +process { + + withName: SAMTOOLS_INDEX { + ext.args = '-c' + } + +} diff --git a/modules/nf-core/samtools/index/tests/main.nf.test b/modules/nf-core/samtools/index/tests/main.nf.test new file mode 100644 index 00000000..c76a9169 --- /dev/null +++ b/modules/nf-core/samtools/index/tests/main.nf.test @@ -0,0 +1,87 @@ +nextflow_process { + + name "Test Process SAMTOOLS_INDEX" + script "../main.nf" + process "SAMTOOLS_INDEX" + tag "modules" + tag "modules_nfcore" + tag "samtools" + tag "samtools/index" + + test("sarscov2 [BAI]") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.test_data['sarscov2']['illumina']['test_paired_end_sorted_bam'], checkIfExists: true) + ] + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out.bai).match("bai") }, + { assert path(process.out.versions.get(0)).getText().contains("samtools") } + ) + } + } + + test("homo_sapiens [CRAI]") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.test_data['homo_sapiens']['illumina']['test_paired_end_recalibrated_sorted_cram'], checkIfExists: true) + ] + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out.crai).match("crai") }, + { assert path(process.out.versions.get(0)).getText().contains("samtools") } + ) + } + } + + test("homo_sapiens [CSI]") { + + config "./csi.nextflow.config" + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.test_data['sarscov2']['illumina']['test_paired_end_sorted_bam'], checkIfExists: true) + ] + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert path(process.out.csi.get(0).get(1)).exists() }, + { assert path(process.out.versions.get(0)).getText().contains("samtools") } + ) + } + } +} diff --git a/modules/nf-core/samtools/index/tests/main.nf.test.snap b/modules/nf-core/samtools/index/tests/main.nf.test.snap new file mode 100644 index 00000000..b3baee7f --- /dev/null +++ b/modules/nf-core/samtools/index/tests/main.nf.test.snap @@ -0,0 +1,28 @@ +{ + "crai": { + "content": [ + [ + [ + { + "id": "test" + }, + "test.paired_end.recalibrated.sorted.cram.crai:md5,14bc3bd5c89cacc8f4541f9062429029" + ] + ] + ], + "timestamp": "2023-11-15T15:17:37.30801" + }, + "bai": { + "content": [ + [ + [ + { + "id": "test" + }, + "test.paired_end.sorted.bam.bai:md5,704c10dd1326482448ca3073fdebc2f4" + ] + ] + ], + "timestamp": "2023-11-15T15:17:30.869234" + } +} \ No newline at end of file diff --git a/modules/nf-core/samtools/index/tests/tags.yml b/modules/nf-core/samtools/index/tests/tags.yml new file mode 100644 index 00000000..e0f58a7a --- /dev/null +++ b/modules/nf-core/samtools/index/tests/tags.yml @@ -0,0 +1,2 @@ +samtools/index: + - modules/nf-core/samtools/index/** diff --git a/subworkflows/local/trim_workflow.nf b/subworkflows/local/trim_workflow.nf index 63012680..ef80ba32 100644 --- a/subworkflows/local/trim_workflow.nf +++ b/subworkflows/local/trim_workflow.nf @@ -9,6 +9,9 @@ workflow TRIM_WORKFLOW { main: ch_versions = Channel.empty() + ch_fastp_html = Channel.empty() + ch_fastp_json = Channel.empty() + ch_fastqc_trimmed = Channel.empty if (params.fastp_trim) { FASTP(reads, params.adapter_fasta, false, false) @@ -19,6 +22,9 @@ workflow TRIM_WORKFLOW { ch_reads_all = FASTP.out.reads ch_reads_fusioncatcher = ch_reads_all + ch_fastp_html = FASTP.out.html + ch_fastp_json = FASTP.out.json + ch_fastqc_trimmed = FASTQC_FOR_FASTP.out.zip } else { @@ -29,6 +35,9 @@ workflow TRIM_WORKFLOW { emit: ch_reads_all ch_reads_fusioncatcher + ch_fastp_html + ch_fastp_json + ch_fastqc_trimmed versions = ch_versions.ifEmpty(null) } diff --git a/workflows/rnafusion.nf b/workflows/rnafusion.nf index cd223444..0cb60d60 100644 --- a/workflows/rnafusion.nf +++ b/workflows/rnafusion.nf @@ -263,9 +263,9 @@ workflow RNAFUSION { ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) - ch_multiqc_files = ch_multiqc_files.mix(FASTP.out.html.collect{it[1]}.ifEmpty([])) - ch_multiqc_files = ch_multiqc_files.mix(FASTP.out.json.collect{it[1]}.ifEmpty([])) - ch_multiqc_files = ch_multiqc_files.mix(FASTQC_FOR_FASTP.out.zip.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(TRIM_WORKFLOW.out.ch_fastp_html.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(TRIM_WORKFLOW.out.ch_fastp_json.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(TRIM_WORKFLOW.out.ch_fastqc_trimmed.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(STARFUSION_WORKFLOW.out.star_stats.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(STARFUSION_WORKFLOW.out.star_gene_count.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.rnaseq_metrics.collect{it[1]}.ifEmpty([])) From 4459e7c5997c426699db802359f8e3955f398a2e Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:13:47 +0100 Subject: [PATCH 151/175] fix typo --- subworkflows/local/trim_workflow.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/trim_workflow.nf b/subworkflows/local/trim_workflow.nf index ef80ba32..ce5244f1 100644 --- a/subworkflows/local/trim_workflow.nf +++ b/subworkflows/local/trim_workflow.nf @@ -11,7 +11,7 @@ workflow TRIM_WORKFLOW { ch_versions = Channel.empty() ch_fastp_html = Channel.empty() ch_fastp_json = Channel.empty() - ch_fastqc_trimmed = Channel.empty + ch_fastqc_trimmed = Channel.empty() if (params.fastp_trim) { FASTP(reads, params.adapter_fasta, false, false) From 5087346bb3d3cf9720489a79500b690f55ee9ce8 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:23:47 +0100 Subject: [PATCH 152/175] fix --- subworkflows/local/fusioninspector_workflow.nf | 2 +- workflows/rnafusion.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/fusioninspector_workflow.nf b/subworkflows/local/fusioninspector_workflow.nf index 3f1f9f13..30f18596 100644 --- a/subworkflows/local/fusioninspector_workflow.nf +++ b/subworkflows/local/fusioninspector_workflow.nf @@ -58,7 +58,7 @@ workflow FUSIONINSPECTOR_WORKFLOW { } emit: - arriba_visualisation = ch_arriba_visualisation.ifEmpty(null) + ch_arriba_visualisation versions = ch_versions.ifEmpty(null) } diff --git a/workflows/rnafusion.nf b/workflows/rnafusion.nf index 0cb60d60..fbf36b31 100644 --- a/workflows/rnafusion.nf +++ b/workflows/rnafusion.nf @@ -271,7 +271,7 @@ workflow RNAFUSION { ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.rnaseq_metrics.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.duplicate_metrics.collect{it[1]}.ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(QC_WORKFLOW.out.insertsize_metrics.collect{it[1]}.ifEmpty([])) - ch_multiqc_files = ch_multiqc_files.mix(FUSIONINSPECTOR_WORKFLOW.out.arriba_visualisation.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(FUSIONINSPECTOR_WORKFLOW.out.ch_arriba_visualisation.collect{it[1]}.ifEmpty([])) From ba1f4e2fbdb4db09b9e53f7befd9c9644d4516f8 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:20:46 +0100 Subject: [PATCH 153/175] fist steps --- bin/vcf_collect.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index b5ff126d..e83204c9 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -158,7 +158,13 @@ def parse_args(argv=None): "--fusionreport", metavar="FUSIONREPORT", type=Path, - help="Fusionreport output in TSV format.", + help="Fusionreport output in index/html format.", + ) + parser.add_argument( + "--fusionreport_csv", + metavar="FUSIONREPORT_CSV", + type=Path, + help="Fusionreport output in CSV format.", ) parser.add_argument( "--fusioninspector_gtf", @@ -303,6 +309,11 @@ def read_build_fusionreport(fusionreport_file: str) -> pd.DataFrame: ["FUSION"] ) +def read_fusionreport_csv(file: str) -> pd.DataFrame: + df = pd.read_csv(file, sep="\t") + + + def column_manipulation(df: pd.DataFrame) -> pd.DataFrame: """ From bccdbc1dc6463e5b65bb81e74ad0d42d71292471 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:38:27 +0100 Subject: [PATCH 154/175] fix paths --- assets/multiqc_config.yml | 6 +++--- conf/modules.config | 4 ++-- subworkflows/local/trim_workflow.nf | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 7e090b4a..b21e8548 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -28,15 +28,15 @@ module_order: name: "FastQC (raw)" info: "This section of the report shows FastQC results before adapter trimming." path_filters: - - "./fastqc/*.zip" + - "*.zip" - fastp - fastqc: name: "FastQC (trimmed)" info: "This section of the report shows FastQC results after adapter trimming." path_filters: - - "./fastqc_for_trim/*.zip" + - "*.zip" - arriba: name: "Arriba visualisation" info: "This section of the report shows Arriba visualisation after FusionInspector analysis." path_filters: - - "./arriba_visualisation/*.pdf" + - "*.pdf" diff --git a/conf/modules.config b/conf/modules.config index 962bb1e9..ef3e69ab 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -75,10 +75,10 @@ process { ] } - withName: FASTQC_FOR_TRIM { + withName: FASTQC_FOR_FASTP { ext.args = '--quiet' publishDir = [ - path: { "${params.outdir}/fastqc_for_trim" }, + path: { "${params.outdir}/fastqc_for_fastp" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] diff --git a/subworkflows/local/trim_workflow.nf b/subworkflows/local/trim_workflow.nf index ce5244f1..61cce26f 100644 --- a/subworkflows/local/trim_workflow.nf +++ b/subworkflows/local/trim_workflow.nf @@ -1,4 +1,3 @@ -include { FASTQC as FASTQC_FOR_TRIM } from '../../modules/nf-core/fastqc/main' include { FASTP } from '../../modules/nf-core/fastp/main' include { FASTQC as FASTQC_FOR_FASTP } from '../../modules/nf-core/fastqc/main' From a0ea41320f579d7046839a57dff637ea6a2da2c6 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:43:46 +0100 Subject: [PATCH 155/175] rename trimmed fastqc files --- assets/multiqc_config.yml | 2 +- conf/modules.config | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index b21e8548..d521c656 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -34,7 +34,7 @@ module_order: name: "FastQC (trimmed)" info: "This section of the report shows FastQC results after adapter trimming." path_filters: - - "*.zip" + - "*_trimmed*.zip" - arriba: name: "Arriba visualisation" info: "This section of the report shows Arriba visualisation after FusionInspector analysis." diff --git a/conf/modules.config b/conf/modules.config index ef3e69ab..dd98ea0e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -77,6 +77,7 @@ process { withName: FASTQC_FOR_FASTP { ext.args = '--quiet' + ext.prefix = { "${meta.id}_trimmed" } publishDir = [ path: { "${params.outdir}/fastqc_for_fastp" }, mode: params.publish_dir_mode, From 8ed7167a8c81718f1a205d18062cafc4f0275406 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 11:44:47 +0100 Subject: [PATCH 156/175] add csv info from fusionreport csv in vcf_collect to insure position is present and propagate exon nb and transcript id to all entries of gtf as some do not have the info --- bin/vcf_collect.py | 101 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 14 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index e83204c9..2401d2e8 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -11,11 +11,15 @@ logger = logging.getLogger() -# vcf_collect(args.fusioninspector, args.fusionreport, args.fusioninspector_gtf, args.hgnc, args.sample, args.out) - def vcf_collect( - fusioninspector_in_file: str, fusionreport_in_file: str, gtf: str, hgnc: str, sample: str, out_file + fusioninspector_in_file: str, + fusionreport_in_file: str, + gtf: str, + fusionreport_csv: str, + hgnc: str, + sample: str, + out_file, ) -> None: """ Process FusionInspector and FusionReport data, @@ -24,10 +28,11 @@ def vcf_collect( Args: fusioninspector_in_file (str): Path to FusionInspector input file. - fusionreport_in_file (str): Path to FusionReport input file. + fusionreport_in_file (str): Path to Fusion-report input file. sample (str): Sample name for the header. hgnc (str): Path to HGNC file. gtf (str): Path to output GTF file from FusionInspector in TSV format. + fusionreport_csv (str): Path to Fusion-report CSV output file. out (str): Output VCF file path. Adapted from: https://github.com/J35P312/MegaFusion @@ -67,6 +72,19 @@ def vcf_collect( ((all_df["PosA"] >= all_df["orig_start"]) & (all_df["PosA"] <= all_df["orig_end"])) | ((all_df["orig_start"] == 0) & (all_df["orig_end"] == 0)) ] + + all_df.replace("", np.nan, inplace=True) + all_df = all_df.drop_duplicates() + + all_df[["exon_number", "transcript_version"]] = all_df[["exon_number", "transcript_version"]].replace(0, np.nan) + # Fill non-empty values within each group for 'exon_number' and 'transcript_version' + all_df["exon_number"] = all_df.groupby("PosA")["exon_number"].transform( + lambda x: x.fillna(method="ffill").fillna(method="bfill") + ) + all_df["transcript_version"] = all_df.groupby("PosA")["transcript_version"].transform( + lambda x: x.fillna(method="ffill").fillna(method="bfill") + ) + all_df = all_df.rename(columns={"transcript_version": "Left_transcript_version"}) all_df = all_df.rename(columns={"exon_number": "Left_exon_number"}) all_df = all_df[ @@ -105,6 +123,18 @@ def vcf_collect( | ((all_df["orig_start"] == 0) & (all_df["orig_end"] == 0)) ] + all_df[["PosA", "PosB"]] = all_df[["PosA", "PosB"]].replace(0, np.nan) + all_df = all_df.replace("", np.nan) + + all_df[["exon_number", "transcript_version"]] = all_df[["exon_number", "transcript_version"]].replace(0, np.nan) + # Fill non-empty values within each group for 'exon_number' and 'transcript_version' + all_df["exon_number"] = all_df.groupby("PosB")["exon_number"].transform( + lambda x: x.fillna(method="ffill").fillna(method="bfill") + ) + all_df["transcript_version"] = all_df.groupby("PosB")["transcript_version"].transform( + lambda x: x.fillna(method="ffill").fillna(method="bfill") + ) + all_df = all_df.rename(columns={"transcript_version": "Right_transcript_version"}) all_df = all_df.rename(columns={"exon_number": "Right_exon_number"}) @@ -138,6 +168,10 @@ def vcf_collect( "annots", ] ].drop_duplicates() + all_df = all_df.rename(columns={"FUSION": "Fusion"}) + all_df = all_df.set_index("Fusion") + + all_df = all_df.combine_first(read_fusionreport_csv(fusionreport_csv)) return write_vcf(column_manipulation(all_df), header_def(sample), out_file) @@ -309,10 +343,39 @@ def read_build_fusionreport(fusionreport_file: str) -> pd.DataFrame: ["FUSION"] ) -def read_fusionreport_csv(file: str) -> pd.DataFrame: - df = pd.read_csv(file, sep="\t") - +def read_fusionreport_csv(file: str) -> pd.DataFrame: + df = pd.read_csv(file) + df[["starfusion", "arriba", "fusioncatcher"]] = df[["starfusion", "arriba", "fusioncatcher"]].astype("str") + columns_to_iterate = ["starfusion", "arriba", "fusioncatcher"] + for index, row in df.iterrows(): + for column in columns_to_iterate: + cell_value = row[column] + + if "#" in cell_value: + df.at[index, column] = df.at[index, column].split(",")[0] + df.at[index, column] = df.at[index, column].replace("position: ", "") + df.at[index, "A"] = df.at[index, column].split("#")[0] + df.at[index, "B"] = df.at[index, column].split("#")[1] + df.at[index, "ChromosomeA"] = df.at[index, "A"].split(":")[0] + df.at[index, "PosA"] = df.at[index, "A"].split(":")[1] + if "+" in df.at[index, "A"] or "-" in df.at[index, "A"]: + df.at[index, "StrandA"] = df.at[index, "A"].split(":")[2] + else: + df.at[index, "StrandA"] = "" + + df.at[index, "ChromosomeB"] = df.at[index, "B"].split(":")[0] + df.at[index, "PosB"] = df.at[index, "B"].split(":")[1] + if "+" in df.at[index, "B"] or "-" in df.at[index, "B"]: + df.at[index, "StrandB"] = df.at[index, "B"].split(":")[2] + else: + df.at[index, "StrandB"] = "" + + break + df[["GeneA", "GeneB"]] = df["Fusion"].str.split("--", expand=True) + df = df.set_index("Fusion") + df.to_csv("tmp.csv") + return df[["GeneA", "GeneB", "ChromosomeA", "PosA", "StrandA", "ChromosomeB", "PosB", "StrandB"]] def column_manipulation(df: pd.DataFrame) -> pd.DataFrame: @@ -340,13 +403,14 @@ def column_manipulation(df: pd.DataFrame) -> pd.DataFrame: df["Right_exon_number"] = df["Right_exon_number"].fillna(0).astype(int).astype(str) df["Left_transcript_version"] = df["Left_transcript_version"].fillna(0).astype(int).astype(str) df["Right_transcript_version"] = df["Right_transcript_version"].fillna(0).astype(int).astype(str) + df["PosA"] = df["PosA"].fillna(0).astype(int).astype(str) + df["PosB"] = df["PosB"].fillna(0).astype(int).astype(str) + df["PROT_FUSION_TYPE"] = df["PROT_FUSION_TYPE"].replace(".", "nan") + df["CDS_LEFT_ID"] = df["CDS_LEFT_ID"].replace(".", "nan") + df["CDS_RIGHT_ID"] = df["CDS_RIGHT_ID"].replace(".", "nan") for index, row in df.iterrows(): - if row["Strand1"] == "nan": - df.loc[index, "ALT"] = "nan" - elif not row["Strand1"] in ["+", "-"] or not row["Strand2"] in ["+", "-"]: - df.loc[index, "ALT"] = f'N[{df["ChromosomeB"]}:{row["PosB"]}[' - elif row["Strand1"] == "-" and row["Strand2"] == "-": + if row["Strand1"] == "-" and row["Strand2"] == "-": df.loc[index, "ALT"] = f'[{row["ChromosomeB"]}:{row["PosB"]}[N' elif row["Strand1"] == "+" and row["Strand2"] == "-": df.loc[index, "ALT"] = f'N]{row["ChromosomeB"]}:{row["PosB"]}]' @@ -411,7 +475,7 @@ def build_gtf_dataframe(file: str) -> pd.DataFrame: df = pd.read_csv(file, sep="\t") df[["fusion_dump", "Transcript_id"]] = df["transcript_id"].str.split("^", expand=True) df[["orig_chromosome", "orig_start", "orig_end", "orig_dir"]] = df["orig_coord_info"].str.split(",", expand=True) - return df[["Transcript_id", "transcript_version", "exon_number", "exon_id", "orig_start", "orig_end"]] + return df[["Transcript_id", "transcript_version", "exon_number", "orig_start", "orig_end"]] def main(argv=None): @@ -421,11 +485,20 @@ def main(argv=None): not args.fusioninspector.is_file() or not args.fusionreport.is_file() or not args.fusioninspector_gtf + or not args.fusionreport_csv or not args.hgnc ): logger.error(f"The given input file {args.fusioninspector} or {args.fusionreport} was not found!") sys.exit(2) - vcf_collect(args.fusioninspector, args.fusionreport, args.fusioninspector_gtf, args.hgnc, args.sample, args.out) + vcf_collect( + args.fusioninspector, + args.fusionreport, + args.fusioninspector_gtf, + args.fusionreport_csv, + args.hgnc, + args.sample, + args.out, + ) if __name__ == "__main__": From e91639db52e5700be5180f0eca85aebad169f39f Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 11:48:20 +0100 Subject: [PATCH 157/175] add channel i/o for new input fusionreport csv --- modules/local/vcf_collect/main.nf | 4 ++-- subworkflows/local/fusioninspector_workflow.nf | 3 ++- workflows/rnafusion.nf | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/local/vcf_collect/main.nf b/modules/local/vcf_collect/main.nf index 2af4a777..3ede7936 100644 --- a/modules/local/vcf_collect/main.nf +++ b/modules/local/vcf_collect/main.nf @@ -8,7 +8,7 @@ process VCF_COLLECT { 'quay.io/biocontainers/pandas:1.5.2' }" input: - tuple val(meta), path(fusioninspector_tsv), path(fusioninspector_gtf_tsv), path(fusionreport_report) + tuple val(meta), path(fusioninspector_tsv), path(fusioninspector_gtf_tsv), path(fusionreport_report), path(fusionreport_csv) tuple val(meta2), path(hgnc_ref) tuple val(meta3), path(hgnc_date) @@ -22,7 +22,7 @@ process VCF_COLLECT { script: def prefix = task.ext.prefix ?: "${meta.id}" """ - vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}_fusion_data.vcf + vcf_collect.py --fusioninspector $fusioninspector_tsv --fusionreport $fusionreport_report --fusioninspector_gtf $fusioninspector_gtf_tsv --fusionreport_csv $fusionreport_csv --hgnc $hgnc_ref --sample ${prefix} --out ${prefix}_fusion_data.vcf gzip ${prefix}_fusion_data.vcf cat <<-END_VERSIONS > versions.yml diff --git a/subworkflows/local/fusioninspector_workflow.nf b/subworkflows/local/fusioninspector_workflow.nf index 32173e81..b2d7d791 100644 --- a/subworkflows/local/fusioninspector_workflow.nf +++ b/subworkflows/local/fusioninspector_workflow.nf @@ -10,6 +10,7 @@ workflow FUSIONINSPECTOR_WORKFLOW { fusion_list fusion_list_filtered fusionreport_out + fusionreport_csv bam_sorted_indexed ch_gtf ch_arriba_ref_protein_domains @@ -45,7 +46,7 @@ workflow FUSIONINSPECTOR_WORKFLOW { AGAT_CONVERTSPGFF2TSV(FUSIONINSPECTOR.out.out_gtf) ch_versions = ch_versions.mix(AGAT_CONVERTSPGFF2TSV.out.versions) - fusion_data = FUSIONINSPECTOR.out.tsv_coding_effect.join(AGAT_CONVERTSPGFF2TSV.out.tsv).join(fusionreport_out) + fusion_data = FUSIONINSPECTOR.out.tsv_coding_effect.join(AGAT_CONVERTSPGFF2TSV.out.tsv).join(fusionreport_out).join(fusionreport_csv) VCF_COLLECT(fusion_data, ch_hgnc_ref, ch_hgnc_date) ch_versions = ch_versions.mix(VCF_COLLECT.out.versions) diff --git a/workflows/rnafusion.nf b/workflows/rnafusion.nf index 6792eff8..5a587eeb 100644 --- a/workflows/rnafusion.nf +++ b/workflows/rnafusion.nf @@ -222,6 +222,7 @@ workflow RNAFUSION { FUSIONREPORT_WORKFLOW.out.fusion_list, FUSIONREPORT_WORKFLOW.out.fusion_list_filtered, FUSIONREPORT_WORKFLOW.out.report, + FUSIONREPORT_WORKFLOW.out.csv, STARFUSION_WORKFLOW.out.ch_bam_sorted_indexed, ch_chrgtf, ch_arriba_ref_protein_domains, From a568a9c7a7d526ac36ec296640117c42b5085ee9 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 11:50:51 +0100 Subject: [PATCH 158/175] fusioninspector not sensititve --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index 39135aa3..61535a7e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -94,7 +94,7 @@ process { withName: FUSIONINSPECTOR { ext.when = { !params.skip_vis } ext.args = { params.fusioninspector_limitSjdbInsertNsj != 1000000 ? "--STAR_xtra_params \"--limitSjdbInsertNsj ${params.fusioninspector_limitSjdbInsertNsj}\"" : '' } - ext.args2 = '--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect' + ext.args2 = '--annotate --examine_coding_effect' } withName: FUSIONREPORT { From 15e4a54e87f56cb029a81a41430ec1aa67fde399 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:55:57 +0100 Subject: [PATCH 159/175] fix channel io --- subworkflows/local/fusionreport_workflow.nf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/subworkflows/local/fusionreport_workflow.nf b/subworkflows/local/fusionreport_workflow.nf index 92dc8f42..632ba2a3 100644 --- a/subworkflows/local/fusionreport_workflow.nf +++ b/subworkflows/local/fusionreport_workflow.nf @@ -12,6 +12,7 @@ workflow FUSIONREPORT_WORKFLOW { main: ch_versions = Channel.empty() ch_report = Channel.empty() + ch_csv = Channel.empty() if (!params.fusioninspector_only) { reads_fusions = reads @@ -24,6 +25,7 @@ workflow FUSIONREPORT_WORKFLOW { ch_fusion_list_filtered = FUSIONREPORT.out.fusion_list_filtered ch_versions = ch_versions.mix(FUSIONREPORT.out.versions) ch_report = FUSIONREPORT.out.report + ch_csv = FUSIONREPORT.out.csv } else { ch_fusion_list = reads.combine(Channel.value(file(params.fusioninspector_fusions, checkIfExists:true))) .map { meta, reads, fusions -> [ meta, fusions ] } @@ -35,6 +37,8 @@ workflow FUSIONREPORT_WORKFLOW { versions = ch_versions.ifEmpty(null) fusion_list = ch_fusion_list fusion_list_filtered = ch_fusion_list_filtered - report = ch_report.ifEmpty(null) + report = ch_report.ifEmpty(null) + csv = ch_csv.ifEmpty(null) + } From 716a852dc1466fdc91b87f09936131e8a7c3fc95 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:02:49 +0100 Subject: [PATCH 160/175] revert vcf --- bin/vcf_collect.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/bin/vcf_collect.py b/bin/vcf_collect.py index 5d359107..b5ff126d 100755 --- a/bin/vcf_collect.py +++ b/bin/vcf_collect.py @@ -63,24 +63,10 @@ def vcf_collect( all_df = df.merge(gtf_df, how="left", left_on="CDS_LEFT_ID", right_on="Transcript_id") all_df[["PosA", "orig_start", "orig_end"]] = all_df[["PosA", "orig_start", "orig_end"]].fillna(0).astype(int) - all_df["bla"] = ((all_df["PosA"] >= all_df["orig_start"]) & (all_df["PosA"] <= all_df["orig_end"])) | ( - (all_df["orig_start"] == 0) & (all_df["orig_end"] == 0) - ) - all_df = all_df[ ((all_df["PosA"] >= all_df["orig_start"]) & (all_df["PosA"] <= all_df["orig_end"])) | ((all_df["orig_start"] == 0) & (all_df["orig_end"] == 0)) ] - - all_df.replace("", np.nan, inplace=True) - # Fill non-empty values within each group for 'exon_number' and 'transcript_version' - all_df["exon_number"] = all_df.groupby("PosA")["exon_number"].transform( - lambda x: x.fillna(method="ffill").fillna(method="bfill") - ) - all_df["transcript_version"] = all_df.groupby("PosA")["transcript_version"].transform( - lambda x: x.fillna(method="ffill").fillna(method="bfill") - ) - all_df = all_df.rename(columns={"transcript_version": "Left_transcript_version"}) all_df = all_df.rename(columns={"exon_number": "Left_exon_number"}) all_df = all_df[ From a76b16282e8d6a6bddde952396b9c45732dff412 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:03:36 +0100 Subject: [PATCH 161/175] revert fusioninspector filtering --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index dd98ea0e..90759b92 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -100,7 +100,7 @@ process { withName: FUSIONINSPECTOR { ext.when = { !params.skip_vis } ext.args = { params.fusioninspector_limitSjdbInsertNsj != 1000000 ? "--STAR_xtra_params \"--limitSjdbInsertNsj ${params.fusioninspector_limitSjdbInsertNsj}\"" : '' } - ext.args2 = '--annotate --examine_coding_effect' + ext.args2 = '--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect' } withName: FUSIONREPORT { From 04d57f07dc1f438f0c150413d316a08c17142ff1 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:02:06 +0100 Subject: [PATCH 162/175] Update assets/multiqc_config.yml Co-authored-by: Eva C <29628428+fevac@users.noreply.github.com> --- assets/multiqc_config.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index d521c656..2b2a1e5e 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -35,8 +35,3 @@ module_order: info: "This section of the report shows FastQC results after adapter trimming." path_filters: - "*_trimmed*.zip" - - arriba: - name: "Arriba visualisation" - info: "This section of the report shows Arriba visualisation after FusionInspector analysis." - path_filters: - - "*.pdf" From 3fd02892631ebb4175780408e6793afbd9e65e34 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:02:41 +0100 Subject: [PATCH 163/175] Update assets/multiqc_config.yml Co-authored-by: Eva C <29628428+fevac@users.noreply.github.com> --- assets/multiqc_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 2b2a1e5e..5930000d 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -24,12 +24,12 @@ run_modules: - arriba module_order: + - fastp - fastqc: name: "FastQC (raw)" info: "This section of the report shows FastQC results before adapter trimming." path_filters: - "*.zip" - - fastp - fastqc: name: "FastQC (trimmed)" info: "This section of the report shows FastQC results after adapter trimming." From 113dd9e01346ac2eb48f1365e332acab37f56702 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:05:11 +0100 Subject: [PATCH 164/175] udpate changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c40c613c..5116c32a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix channel i/o issue in StringTie workflow and add StringTie in github CI tests [#416](https://github.com/nf-core/rnafusion/pull/416) - Updated COSMIC database to fix 404 error while downloading fusionreport references [#420](https://github.com/nf-core/rnafusion/pull/420) +- Update modules, and make sure MultiQC displays the QC results properly [#440](https://github.com/nf-core/rnafusion/pull/440) ### Removed From 652cbd73098d76055615a3afa9cf11eb9d6c20ae Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:38:28 +0100 Subject: [PATCH 165/175] add conditions for collectinsertsize running --- conf/modules.config | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/modules.config b/conf/modules.config index 39135aa3..b7b5445c 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -165,6 +165,7 @@ process { } withName: PICARD_COLLECTINSERTSIZEMETRICS { + ext.when = { !params.skip_qc && !params.fusioninspector_only && (params.starfusion || params.all) } ext.prefix = { "${meta.id}_collectinsertsize"} publishDir = [ path: { "${params.outdir}/picard" }, From dead314a02148e295b1f826f128e04375a2ad7ec Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:39:55 +0100 Subject: [PATCH 166/175] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c40c613c..612e0ea1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix channel i/o issue in StringTie workflow and add StringTie in github CI tests [#416](https://github.com/nf-core/rnafusion/pull/416) - Updated COSMIC database to fix 404 error while downloading fusionreport references [#420](https://github.com/nf-core/rnafusion/pull/420) +- Add 'when' condition to run collectinsertsize [#444](https://github.com/nf-core/rnafusion/pull/444) ### Removed From 635bee670671fa130f4f0a6cd11ab9328b0d2b15 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:10:00 +0100 Subject: [PATCH 167/175] update fusionreport --- CHANGELOG.md | 1 + modules/local/fusionreport/detect/main.nf | 2 +- modules/local/fusionreport/download/main.nf | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 612e0ea1..1351fba3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - CI tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#430](https://github.com/nf-core/rnafusion/pull/430) - AWS tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#433](https://github.com/nf-core/rnafusion/pull/433) - Update `fusion-report` to 2.1.5p8 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) +- Update `fusion-report` to 2.1.8, removing FusionGDB database [#445](https://github.com/nf-core/rnafusion/pull/445) ### Fixed diff --git a/modules/local/fusionreport/detect/main.nf b/modules/local/fusionreport/detect/main.nf index 6060e0ae..8024d8f8 100644 --- a/modules/local/fusionreport/detect/main.nf +++ b/modules/local/fusionreport/detect/main.nf @@ -3,7 +3,7 @@ process FUSIONREPORT { label 'process_medium' conda "bioconda::star=2.7.9a" - container "docker.io/clinicalgenomics/fusion-report:2.1.5p8" + container "docker.io/clinicalgenomics/fusion-report:2.1.8" input: diff --git a/modules/local/fusionreport/download/main.nf b/modules/local/fusionreport/download/main.nf index 220b51c9..ac288ade 100644 --- a/modules/local/fusionreport/download/main.nf +++ b/modules/local/fusionreport/download/main.nf @@ -3,7 +3,7 @@ process FUSIONREPORT_DOWNLOAD { label 'process_medium' conda "bioconda::star=2.7.9a" - container "docker.io/clinicalgenomics/fusion-report:2.1.5p8" + container "docker.io/clinicalgenomics/fusion-report:2.1.8" input: val(username) From 4a216e796eabd6cdad838b6869788f34fe223c0b Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:27:23 +0100 Subject: [PATCH 168/175] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c40c613c..a79e6013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - CI tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#430](https://github.com/nf-core/rnafusion/pull/430) - AWS tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#433](https://github.com/nf-core/rnafusion/pull/433) - Update `fusion-report` to 2.1.5p8 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) +- Update vcf creation to get positions/chromosomes and strands even when fusions are filtered out by fusioninspector, using the csv output from fusion-report [#443](https://github.com/nf-core/rnafusion/pull/443) ### Fixed From 459684900345f7745925b689789128aee89aaaf4 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:39:49 +0100 Subject: [PATCH 169/175] update doc --- CHANGELOG.md | 1 + docs/output.md | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95813b7f..c9e18d9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - AWS tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#433](https://github.com/nf-core/rnafusion/pull/433) - Update `fusion-report` to 2.1.5p8 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) - Update `fusion-report` to 2.1.8, removing FusionGDB database [#445](https://github.com/nf-core/rnafusion/pull/445) +- Update documentation [#446](https://github.com/nf-core/rnafusion/pull/446) ### Fixed diff --git a/docs/output.md b/docs/output.md index 559e5fb2..dede2c87 100644 --- a/docs/output.md +++ b/docs/output.md @@ -222,9 +222,8 @@ The score is explained here: Date: Fri, 24 Nov 2023 15:41:15 +0100 Subject: [PATCH 170/175] update changelof --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ee977e..bfcfa21a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - CI tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#430](https://github.com/nf-core/rnafusion/pull/430) - AWS tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#433](https://github.com/nf-core/rnafusion/pull/433) - Update `fusion-report` to 2.1.5p8 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) -- Update vcf creation to get positions/chromosomes and strands even when fusions are filtered out by fusioninspector, using the csv output from fusion-report [#443](https://github.com/nf-core/rnafusion/pull/443) +- Remove `--max_sensitivity --max_mate_dist 10000000` from fusioninspector. Update vcf creation to get positions/chromosomes and strands even when fusions are filtered out by fusioninspector, using the csv output from fusion-report [#443](https://github.com/nf-core/rnafusion/pull/443) ### Fixed From df7a7b33583e2cebfa5218d3f19d6506926559e7 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:01:58 +0100 Subject: [PATCH 171/175] Update CHANGELOG.md Co-authored-by: Alexander Peltzer --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6636747..2c0a4278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## v3.0.0dev - [date] +## v3.0.0 - [2023-11-28] ### Added From c4526a906a636ba7e38f9e9516d25ba2d8b27e9c Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:12:38 +0100 Subject: [PATCH 172/175] Update tower.yml Co-authored-by: Eva C <29628428+fevac@users.noreply.github.com> --- tower.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tower.yml b/tower.yml index 66b54368..2edf5a7f 100644 --- a/tower.yml +++ b/tower.yml @@ -13,7 +13,7 @@ reports: display: "FusionInspector TSV report" "**/fusionreport/*/*_fusionreport_index.html": display: "Fusion-report HTML report" - "**/vcf_collect/*_fusion_data.vcf": + "**/vcf/*_fusion_data.vcf.gz": display: "Collected statistics on each fusion fed to FusionInspector in VCF format" "**/picard/*.MarkDuplicates.metrics.txt": display: "Picard: Metrics from CollectRnaMetrics" From cbf73a1093dfaa80467c3efe1d9f97cdf8f9d42f Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:15:30 +0100 Subject: [PATCH 173/175] bump version and remove igenomes param --- assets/multiqc_config.yml | 4 ++-- nextflow.config | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 5930000d..6e00ca8c 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,7 +1,7 @@ report_comment: > - This report has been generated by the nf-core/rnafusion + This report has been generated by the nf-core/rnafusion analysis pipeline. For information about how to interpret these results, please see the - documentation. + documentation. report_section_order: nf-core-rnafusion-methods-description: diff --git a/nextflow.config b/nextflow.config index ebaafb30..7570a93b 100644 --- a/nextflow.config +++ b/nextflow.config @@ -115,7 +115,7 @@ params { // Schema validation default options validationFailUnrecognisedParams = false validationLenientMode = false - validationSchemaIgnoreParams = 'genomes,igenomes_base' + validationSchemaIgnoreParams = 'genomes' validationShowHiddenParams = false validate_params = true } @@ -290,7 +290,7 @@ manifest { description = """Nextflow rnafusion analysis pipeline, part of the nf-core community.""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '3.0.0dev' + version = '3.0.0' doi = '' } From 89075bb949dea394667da7e2646002eb60532dd0 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:19:55 +0100 Subject: [PATCH 174/175] update modules --- modules.json | 8 +- modules/nf-core/star/align/main.nf | 4 +- modules/nf-core/star/align/tests/main.nf.test | 339 ++++++++ .../star/align/tests/main.nf.test.snap | 769 ++++++++++++++++++ .../star/align/tests/nextflow.arriba.config | 14 + .../nf-core/star/align/tests/nextflow.config | 14 + .../align/tests/nextflow.starfusion.config | 14 + modules/nf-core/star/align/tests/tags.yml | 2 + modules/nf-core/star/genomegenerate/main.nf | 4 +- .../star/genomegenerate/tests/main.nf.test | 38 + .../genomegenerate/tests/main.nf.test.snap | 16 + .../star/genomegenerate/tests/tags.yml | 2 + .../stringtie/merge/tests/main.nf.test | 82 ++ .../stringtie/merge/tests/main.nf.test.snap | 34 + .../nf-core/stringtie/merge/tests/tags.yml | 2 + .../stringtie/stringtie/tests/main.nf.test | 108 +++ .../stringtie/tests/main.nf.test.snap | 186 +++++ .../stringtie/stringtie/tests/tags.yml | 2 + nextflow_schema.json | 1 - 19 files changed, 1630 insertions(+), 9 deletions(-) create mode 100644 modules/nf-core/star/align/tests/main.nf.test create mode 100644 modules/nf-core/star/align/tests/main.nf.test.snap create mode 100644 modules/nf-core/star/align/tests/nextflow.arriba.config create mode 100644 modules/nf-core/star/align/tests/nextflow.config create mode 100644 modules/nf-core/star/align/tests/nextflow.starfusion.config create mode 100644 modules/nf-core/star/align/tests/tags.yml create mode 100644 modules/nf-core/star/genomegenerate/tests/main.nf.test create mode 100644 modules/nf-core/star/genomegenerate/tests/main.nf.test.snap create mode 100644 modules/nf-core/star/genomegenerate/tests/tags.yml create mode 100644 modules/nf-core/stringtie/merge/tests/main.nf.test create mode 100644 modules/nf-core/stringtie/merge/tests/main.nf.test.snap create mode 100644 modules/nf-core/stringtie/merge/tests/tags.yml create mode 100644 modules/nf-core/stringtie/stringtie/tests/main.nf.test create mode 100644 modules/nf-core/stringtie/stringtie/tests/main.nf.test.snap create mode 100644 modules/nf-core/stringtie/stringtie/tests/tags.yml diff --git a/modules.json b/modules.json index d13e5998..345e65b8 100644 --- a/modules.json +++ b/modules.json @@ -92,22 +92,22 @@ }, "star/align": { "branch": "master", - "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", + "git_sha": "9f6b233518f7d9ecdcf24b798b7e491db5424273", "installed_by": ["modules"] }, "star/genomegenerate": { "branch": "master", - "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", + "git_sha": "0e98289b5bec6e3f8f588a8a9d05e8aacc1179a0", "installed_by": ["modules"] }, "stringtie/merge": { "branch": "master", - "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", + "git_sha": "b0dcb44b018d9b2bcb35b1abb7bcd34061bc5a6d", "installed_by": ["modules"] }, "stringtie/stringtie": { "branch": "master", - "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", + "git_sha": "b0dcb44b018d9b2bcb35b1abb7bcd34061bc5a6d", "installed_by": ["modules"] } } diff --git a/modules/nf-core/star/align/main.nf b/modules/nf-core/star/align/main.nf index fa645a6d..cc4f5af5 100644 --- a/modules/nf-core/star/align/main.nf +++ b/modules/nf-core/star/align/main.nf @@ -4,8 +4,8 @@ process STAR_ALIGN { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:1df389393721fc66f3fd8778ad938ac711951107-0' : - 'biocontainers/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:1df389393721fc66f3fd8778ad938ac711951107-0' }" + 'https://depot.galaxyproject.org/singularity/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:019f262d90511939dce2dca4b7c868fc108f73db-0' : + 'biocontainers/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:019f262d90511939dce2dca4b7c868fc108f73db-0' }" input: tuple val(meta), path(reads, stageAs: "input*/*") diff --git a/modules/nf-core/star/align/tests/main.nf.test b/modules/nf-core/star/align/tests/main.nf.test new file mode 100644 index 00000000..4c878474 --- /dev/null +++ b/modules/nf-core/star/align/tests/main.nf.test @@ -0,0 +1,339 @@ +nextflow_process { + + name "Test Process STAR_ALIGN" + script "../main.nf" + process "STAR_ALIGN" + tag "modules" + tag "modules_nfcore" + tag "star" + tag "star/align" + + test("homo_sapiens - single_end") { + config "./nextflow.config" + + setup { + run("STAR_GENOMEGENERATE") { + script "../../../star/genomegenerate/main.nf" + process { + """ + input[0] = Channel.of([ + [ id:'test_fasta' ], + [file(params.test_data['homo_sapiens']['genome']['genome_fasta'], checkIfExists: true)] + ]) + input[1] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + """ + } + } + } + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_1_fastq_gz'], checkIfExists: true) ] + ]) + input[1] = STAR_GENOMEGENERATE.out.index + input[2] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + input[3] = false + input[4] = 'illumina' + input[5] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(file(process.out.log_final[0][1]).name).match("homo_sapiens - single_end - log_final") }, + { assert snapshot(file(process.out.log_out[0][1]).name).match("homo_sapiens - single_end - log_out") }, + { assert snapshot(process.out.bam).match("homo_sapiens - single_end - bam") }, + { assert snapshot(process.out.bam_sorted).match("homo_sapiens - single_end - bam_sorted") }, + { assert snapshot(process.out.bam_transcript).match("homo_sapiens - single_end - bam_transcript") }, + { assert snapshot(process.out.bam_unsorted).match("homo_sapiens - single_end - bam_unsorted") }, + { assert snapshot(process.out.bedgraph).match("homo_sapiens - single_end - bedgraph") }, + { assert snapshot(process.out.fastq).match("homo_sapiens - single_end - fastq") }, + { assert snapshot(process.out.junction).match("homo_sapiens - single_end - junction") }, + { assert snapshot(process.out.log_progress).match("homo_sapiens - single_end - log_progress") }, + { assert snapshot(process.out.read_per_gene_tab).match("homo_sapiens - single_end - read_per_gene_tab") }, + { assert snapshot(process.out.sam).match("homo_sapiens - single_end - sam") }, + { assert snapshot(process.out.spl_junc_tab).match("homo_sapiens - single_end - spl_junc_tab") }, + { assert snapshot(process.out.tab).match("homo_sapiens - single_end - tab") }, + { assert snapshot(process.out.wig).match("homo_sapiens - single_end - wig") }, + { assert snapshot(process.out.versions).match("homo_sapiens - single_end - versions") } + ) + } + } + + test("homo_sapiens - paired_end") { + config "./nextflow.config" + + setup { + run("STAR_GENOMEGENERATE") { + script "../../../star/genomegenerate/main.nf" + process { + """ + input[0] = Channel.of([ + [ id:'test_fasta' ], + [file(params.test_data['homo_sapiens']['genome']['genome_fasta'], checkIfExists: true)] + ]) + input[1] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + """ + } + } + } + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_1_fastq_gz'], checkIfExists: true), + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_2_fastq_gz'], checkIfExists: true) + ] + ]) + input[1] = STAR_GENOMEGENERATE.out.index + input[2] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + input[3] = false + input[4] = 'illumina' + input[5] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(file(process.out.log_final[0][1]).name).match("homo_sapiens - paired_end - log_final") }, + { assert snapshot(file(process.out.log_out[0][1]).name).match("homo_sapiens - paired_end - log_out") }, + { assert snapshot(process.out.bam).match("homo_sapiens - paired_end - bam") }, + { assert snapshot(process.out.bam_sorted).match("homo_sapiens - paired_end - bam_sorted") }, + { assert snapshot(process.out.bam_transcript).match("homo_sapiens - paired_end - bam_transcript") }, + { assert snapshot(process.out.bam_unsorted).match("homo_sapiens - paired_end - bam_unsorted") }, + { assert snapshot(process.out.bedgraph).match("homo_sapiens - paired_end - bedgraph") }, + { assert snapshot(process.out.fastq).match("homo_sapiens - paired_end - fastq") }, + { assert snapshot(process.out.junction).match("homo_sapiens - paired_end - junction") }, + { assert snapshot(process.out.log_progress).match("homo_sapiens - paired_end - log_progress") }, + { assert snapshot(process.out.read_per_gene_tab).match("homo_sapiens - paired_end - read_per_gene_tab") }, + { assert snapshot(process.out.sam).match("homo_sapiens - paired_end - sam") }, + { assert snapshot(process.out.spl_junc_tab).match("homo_sapiens - paired_end - spl_junc_tab") }, + { assert snapshot(process.out.tab).match("homo_sapiens - paired_end - tab") }, + { assert snapshot(process.out.wig).match("homo_sapiens - paired_end - wig") }, + { assert snapshot(process.out.versions).match("homo_sapiens - paired_end - versions") } + ) + } + } + + test("homo_sapiens - paired_end - arriba") { + config "./nextflow.arriba.config" + + setup { + run("STAR_GENOMEGENERATE") { + script "../../../star/genomegenerate/main.nf" + process { + """ + input[0] = Channel.of([ + [ id:'test_fasta' ], + [file(params.test_data['homo_sapiens']['genome']['genome_fasta'], checkIfExists: true)] + ]) + input[1] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + """ + } + } + } + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_1_fastq_gz'], checkIfExists: true), + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_2_fastq_gz'], checkIfExists: true) + ] + ]) + input[1] = STAR_GENOMEGENERATE.out.index + input[2] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + input[3] = false + input[4] = 'illumina' + input[5] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(file(process.out.log_final[0][1]).name).match("homo_sapiens - paired_end - arriba - log_final") }, + { assert snapshot(file(process.out.log_out[0][1]).name).match("homo_sapiens - paired_end - arriba - log_out") }, + { assert snapshot(file(process.out.log_progress[0][1]).name).match("homo_sapiens - paired_end - arriba - log_progress") }, + { assert snapshot(process.out.bam).match("homo_sapiens - paired_end - arriba - bam") }, + { assert snapshot(process.out.bam_sorted).match("homo_sapiens - paired_end - arriba - bam_sorted") }, + { assert snapshot(process.out.bam_transcript).match("homo_sapiens - paired_end - arriba - bam_transcript") }, + { assert snapshot(process.out.bam_unsorted).match("homo_sapiens - paired_end - arriba - bam_unsorted") }, + { assert snapshot(process.out.bedgraph).match("homo_sapiens - paired_end - arriba - bedgraph") }, + { assert snapshot(process.out.fastq).match("homo_sapiens - paired_end - arriba - fastq") }, + { assert snapshot(process.out.junction).match("homo_sapiens - paired_end - arriba - junction") }, + { assert snapshot(process.out.read_per_gene_tab).match("homo_sapiens - paired_end - arriba - read_per_gene_tab") }, + { assert snapshot(process.out.sam).match("homo_sapiens - paired_end - arriba - sam") }, + { assert snapshot(process.out.spl_junc_tab).match("homo_sapiens - paired_end - arriba - spl_junc_tab") }, + { assert snapshot(process.out.tab).match("homo_sapiens - paired_end - arriba - tab") }, + { assert snapshot(process.out.wig).match("homo_sapiens - paired_end - arriba - wig") }, + { assert snapshot(process.out.versions).match("homo_sapiens - paired_end - arriba - versions") } + ) + } + } + + test("homo_sapiens - paired_end - starfusion") { + config "./nextflow.starfusion.config" + + setup { + run("STAR_GENOMEGENERATE") { + script "../../../star/genomegenerate/main.nf" + process { + """ + input[0] = Channel.of([ + [ id:'test_fasta' ], + [file(params.test_data['homo_sapiens']['genome']['genome_fasta'], checkIfExists: true)] + ]) + input[1] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + """ + } + } + } + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_1_fastq_gz'], checkIfExists: true), + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_2_fastq_gz'], checkIfExists: true) + ] + ]) + input[1] = STAR_GENOMEGENERATE.out.index + input[2] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + input[3] = false + input[4] = 'illumina' + input[5] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(file(process.out.log_final[0][1]).name).match("homo_sapiens - paired_end - starfusion - log_final") }, + { assert snapshot(file(process.out.log_out[0][1]).name).match("homo_sapiens - paired_end - starfusion - log_out") }, + { assert snapshot(file(process.out.log_progress[0][1]).name).match("homo_sapiens - paired_end - starfusion - log_progress") }, + { assert snapshot(process.out.bam).match("homo_sapiens - paired_end - starfusion - bam") }, + { assert snapshot(process.out.bam_sorted).match("homo_sapiens - paired_end - starfusion - bam_sorted") }, + { assert snapshot(process.out.bam_transcript).match("homo_sapiens - paired_end - starfusion - bam_transcript") }, + { assert snapshot(process.out.bam_unsorted).match("homo_sapiens - paired_end - starfusion - bam_unsorted") }, + { assert snapshot(process.out.bedgraph).match("homo_sapiens - paired_end - starfusion - bedgraph") }, + { assert snapshot(process.out.fastq).match("homo_sapiens - paired_end - starfusion - fastq") }, + { assert snapshot(process.out.junction).match("homo_sapiens - paired_end - starfusion - junction") }, + { assert snapshot(process.out.read_per_gene_tab).match("homo_sapiens - paired_end - starfusion - read_per_gene_tab") }, + { assert snapshot(process.out.sam).match("homo_sapiens - paired_end - starfusion - sam") }, + { assert snapshot(process.out.spl_junc_tab).match("homo_sapiens - paired_end - starfusion - spl_junc_tab") }, + { assert snapshot(process.out.tab).match("homo_sapiens - paired_end - starfusion - tab") }, + { assert snapshot(process.out.wig).match("homo_sapiens - paired_end - starfusion - wig") }, + { assert snapshot(process.out.versions).match("homo_sapiens - paired_end - starfusion - versions") } + ) + } + } + + test("homo_sapiens - paired_end - multiple") { + config "./nextflow.config" + + setup { + run("STAR_GENOMEGENERATE") { + script "../../../star/genomegenerate/main.nf" + process { + """ + input[0] = Channel.of([ + [ id:'test_fasta' ], + [file(params.test_data['homo_sapiens']['genome']['genome_fasta'], checkIfExists: true)] + ]) + input[1] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + """ + } + } + } + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_1_fastq_gz'], checkIfExists: true), + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_2_fastq_gz'], checkIfExists: true), + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_1_fastq_gz'], checkIfExists: true), + file(params.test_data['homo_sapiens']['illumina']['test_rnaseq_2_fastq_gz'], checkIfExists: true) + ] + ]) + input[1] = STAR_GENOMEGENERATE.out.index + input[2] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + input[3] = false + input[4] = 'illumina' + input[5] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(file(process.out.log_final[0][1]).name).match("homo_sapiens - paired_end - multiple - log_final") }, + { assert snapshot(file(process.out.log_out[0][1]).name).match("homo_sapiens - paired_end - multiple - log_out") }, + { assert snapshot(file(process.out.log_progress[0][1]).name).match("homo_sapiens - paired_end - multiple - log_progress") }, + { assert snapshot(process.out.bam).match("homo_sapiens - paired_end - multiple - bam") }, + { assert snapshot(process.out.bam_sorted).match("homo_sapiens - paired_end - multiple - bam_sorted") }, + { assert snapshot(process.out.bam_transcript).match("homo_sapiens - paired_end - multiple - bam_transcript") }, + { assert snapshot(process.out.bam_unsorted).match("homo_sapiens - paired_end - multiple - bam_unsorted") }, + { assert snapshot(process.out.bedgraph).match("homo_sapiens - paired_end - multiple - bedgraph") }, + { assert snapshot(process.out.fastq).match("homo_sapiens - paired_end - multiple - fastq") }, + { assert snapshot(process.out.junction).match("homo_sapiens - paired_end - multiple - junction") }, + { assert snapshot(process.out.read_per_gene_tab).match("homo_sapiens - paired_end - multiple - read_per_gene_tab") }, + { assert snapshot(process.out.sam).match("homo_sapiens - paired_end - multiple - sam") }, + { assert snapshot(process.out.spl_junc_tab).match("homo_sapiens - paired_end - multiple - spl_junc_tab") }, + { assert snapshot(process.out.tab).match("homo_sapiens - paired_end - multiple - tab") }, + { assert snapshot(process.out.wig).match("homo_sapiens - paired_end - multiple - wig") }, + { assert snapshot(process.out.versions).match("homo_sapiens - paired_end - multiple - versions") } + ) + } + } +} \ No newline at end of file diff --git a/modules/nf-core/star/align/tests/main.nf.test.snap b/modules/nf-core/star/align/tests/main.nf.test.snap new file mode 100644 index 00000000..59b735d4 --- /dev/null +++ b/modules/nf-core/star/align/tests/main.nf.test.snap @@ -0,0 +1,769 @@ +{ + "homo_sapiens - paired_end - multiple - bam_sorted": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.Aligned.sortedByCoord.out.bam:md5,ab07c21d63ab0a6c07d171d213c81d5a" + ] + ] + ], + "timestamp": "2023-11-23T13:29:01.19639" + }, + "homo_sapiens - paired_end - multiple - wig": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:29:01.857804" + }, + "homo_sapiens - paired_end - arriba - tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.SJ.out.tab:md5,5155c9fd1f787ad6d7d80987fb06219c" + ] + ] + ], + "timestamp": "2023-11-23T13:25:07.396223" + }, + "homo_sapiens - single_end - wig": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:22:55.24701" + }, + "homo_sapiens - paired_end - sam": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:23:33.383818" + }, + "homo_sapiens - paired_end - arriba - versions": { + "content": [ + [ + "versions.yml:md5,452ef035aacbc68d47041e86279a9333" + ] + ], + "timestamp": "2023-11-23T13:25:07.494015" + }, + "homo_sapiens - paired_end - multiple - bedgraph": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test.Signal.Unique.str1.out.bg:md5,d7bf8b70b436ca048a62513e1d0ece3a", + "test.Signal.UniqueMultiple.str1.out.bg:md5,686d58493b9eb445b56ace4d67f76ef6" + ] + ] + ] + ], + "timestamp": "2023-11-23T13:29:01.396383" + }, + "homo_sapiens - paired_end - read_per_gene_tab": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:23:33.368841" + }, + "homo_sapiens - paired_end - arriba - bedgraph": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:25:07.102537" + }, + "homo_sapiens - single_end - junction": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:22:55.185369" + }, + "homo_sapiens - paired_end - arriba - spl_junc_tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.SJ.out.tab:md5,5155c9fd1f787ad6d7d80987fb06219c" + ] + ] + ], + "timestamp": "2023-11-23T13:25:07.348239" + }, + "homo_sapiens - single_end - sam": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:22:55.216183" + }, + "homo_sapiens - paired_end - fastq": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:23:33.327236" + }, + "homo_sapiens - single_end - versions": { + "content": [ + [ + "versions.yml:md5,452ef035aacbc68d47041e86279a9333" + ] + ], + "timestamp": "2023-11-23T13:22:55.259282" + }, + "homo_sapiens - paired_end - multiple - log_out": { + "content": [ + "test.Log.out" + ], + "timestamp": "2023-11-23T13:29:01.022176" + }, + "homo_sapiens - paired_end - arriba - fastq": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:25:07.15277" + }, + "homo_sapiens - paired_end - multiple - junction": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:29:01.52923" + }, + "homo_sapiens - paired_end - multiple - spl_junc_tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.SJ.out.tab:md5,069877e053714e23010fe4e1c003b4a2" + ] + ] + ], + "timestamp": "2023-11-23T13:29:01.729175" + }, + "homo_sapiens - paired_end - starfusion - log_final": { + "content": [ + "test.Log.final.out" + ], + "timestamp": "2023-11-23T13:27:55.905883" + }, + "homo_sapiens - paired_end - starfusion - fastq": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:27:56.192302" + }, + "homo_sapiens - paired_end - multiple - sam": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:29:01.661837" + }, + "homo_sapiens - paired_end - multiple - log_final": { + "content": [ + "test.Log.final.out" + ], + "timestamp": "2023-11-23T13:29:00.966417" + }, + "homo_sapiens - paired_end - starfusion - bam": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.Aligned.out.bam:md5,bcad07b838f6762fc01eea52b5cd3f84" + ] + ] + ], + "timestamp": "2023-11-23T13:27:56.003675" + }, + "homo_sapiens - paired_end - arriba - junction": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:25:07.202776" + }, + "homo_sapiens - single_end - bedgraph": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + [ + "test.Signal.Unique.str1.out.bg:md5,c56fc1472776fb927eaf62d973da5f9a", + "test.Signal.UniqueMultiple.str1.out.bg:md5,e93373cf6f2a2a9506e2efdb260cdd4f" + ] + ] + ] + ], + "timestamp": "2023-11-23T13:22:55.163495" + }, + "homo_sapiens - paired_end - arriba - read_per_gene_tab": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:25:07.251962" + }, + "homo_sapiens - paired_end - starfusion - bam_sorted": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:27:56.040843" + }, + "homo_sapiens - single_end - bam_unsorted": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:22:55.154172" + }, + "homo_sapiens - paired_end - bam": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.Aligned.sortedByCoord.out.bam:md5,b9ee1c607e07323bc1652ef3babb543f" + ] + ] + ], + "timestamp": "2023-11-23T13:23:33.265265" + }, + "homo_sapiens - paired_end - arriba - bam_transcript": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:25:06.998817" + }, + "homo_sapiens - paired_end - log_out": { + "content": [ + "test.Log.out" + ], + "timestamp": "2023-11-23T13:23:33.259699" + }, + "homo_sapiens - paired_end - arriba - log_out": { + "content": [ + "test.Log.out" + ], + "timestamp": "2023-11-23T13:25:06.849451" + }, + "homo_sapiens - paired_end - multiple - versions": { + "content": [ + [ + "versions.yml:md5,452ef035aacbc68d47041e86279a9333" + ] + ], + "timestamp": "2023-11-23T13:29:01.937182" + }, + "homo_sapiens - paired_end - starfusion - bam_transcript": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:27:56.082408" + }, + "homo_sapiens - paired_end - starfusion - tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.SJ.out.tab:md5,19c3faa1bfa9a0cc5e4c45f17065b53a" + ] + ] + ], + "timestamp": "2023-11-23T13:27:56.379367" + }, + "homo_sapiens - single_end - fastq": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:22:55.175307" + }, + "homo_sapiens - paired_end - tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.SJ.out.tab:md5,844af19ab0fc8cd9a3f75228445aca0d" + ] + ] + ], + "timestamp": "2023-11-23T13:23:33.413683" + }, + "homo_sapiens - paired_end - starfusion - bedgraph": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:27:56.155413" + }, + "homo_sapiens - single_end - bam_transcript": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:22:55.144852" + }, + "homo_sapiens - paired_end - versions": { + "content": [ + [ + "versions.yml:md5,452ef035aacbc68d47041e86279a9333" + ] + ], + "timestamp": "2023-11-23T13:23:33.445323" + }, + "homo_sapiens - paired_end - multiple - tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.SJ.out.tab:md5,069877e053714e23010fe4e1c003b4a2" + ] + ] + ], + "timestamp": "2023-11-23T13:29:01.793129" + }, + "homo_sapiens - single_end - bam": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.Aligned.sortedByCoord.out.bam:md5,c6cfaccaf91bc7fdabed3cfe236d4535" + ] + ] + ], + "timestamp": "2023-11-23T13:22:55.128568" + }, + "homo_sapiens - paired_end - arriba - wig": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:25:07.444214" + }, + "homo_sapiens - paired_end - log_progress": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.Log.progress.out:md5,b2bd061d6cbaaf3d6d3b1fed547f69b8" + ] + ] + ], + "timestamp": "2023-11-23T13:23:33.354416" + }, + "homo_sapiens - paired_end - arriba - log_final": { + "content": [ + "test.Log.final.out" + ], + "timestamp": "2023-11-23T13:25:06.829799" + }, + "homo_sapiens - paired_end - bam_unsorted": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:23:33.300509" + }, + "homo_sapiens - paired_end - arriba - sam": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:25:07.300383" + }, + "homo_sapiens - paired_end - multiple - bam": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.Aligned.sortedByCoord.out.bam:md5,ab07c21d63ab0a6c07d171d213c81d5a" + ] + ] + ], + "timestamp": "2023-11-23T13:29:01.13168" + }, + "homo_sapiens - paired_end - multiple - fastq": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:29:01.462257" + }, + "homo_sapiens - single_end - bam_sorted": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.Aligned.sortedByCoord.out.bam:md5,c6cfaccaf91bc7fdabed3cfe236d4535" + ] + ] + ], + "timestamp": "2023-11-23T13:22:55.134799" + }, + "homo_sapiens - paired_end - arriba - bam_sorted": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:25:06.94699" + }, + "homo_sapiens - paired_end - starfusion - junction": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.Chimeric.out.junction:md5,c10ef219f4a30e83711b995bc5e40dba" + ] + ] + ], + "timestamp": "2023-11-23T13:27:56.228327" + }, + "homo_sapiens - single_end - tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.SJ.out.tab:md5,75a516ab950fb958f40b29996474949c" + ] + ] + ], + "timestamp": "2023-11-23T13:22:55.236346" + }, + "homo_sapiens - paired_end - starfusion - versions": { + "content": [ + [ + "versions.yml:md5,452ef035aacbc68d47041e86279a9333" + ] + ], + "timestamp": "2023-11-23T13:27:56.460903" + }, + "homo_sapiens - paired_end - multiple - bam_unsorted": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:29:01.330463" + }, + "homo_sapiens - paired_end - arriba - log_progress": { + "content": [ + "test.Log.progress.out" + ], + "timestamp": "2023-11-23T13:25:06.86866" + }, + "homo_sapiens - paired_end - bedgraph": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test.Signal.Unique.str1.out.bg:md5,d7bf8b70b436ca048a62513e1d0ece3a", + "test.Signal.UniqueMultiple.str1.out.bg:md5,686d58493b9eb445b56ace4d67f76ef6" + ] + ] + ] + ], + "timestamp": "2023-11-23T13:23:33.313258" + }, + "homo_sapiens - paired_end - starfusion - bam_unsorted": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:27:56.118974" + }, + "homo_sapiens - paired_end - starfusion - read_per_gene_tab": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:27:56.264699" + }, + "homo_sapiens - paired_end - multiple - log_progress": { + "content": [ + "test.Log.progress.out" + ], + "timestamp": "2023-11-23T13:29:01.076947" + }, + "homo_sapiens - paired_end - arriba - bam_unsorted": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:25:07.050409" + }, + "homo_sapiens - paired_end - bam_sorted": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.Aligned.sortedByCoord.out.bam:md5,b9ee1c607e07323bc1652ef3babb543f" + ] + ] + ], + "timestamp": "2023-11-23T13:23:33.274809" + }, + "homo_sapiens - single_end - spl_junc_tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.SJ.out.tab:md5,75a516ab950fb958f40b29996474949c" + ] + ] + ], + "timestamp": "2023-11-23T13:22:55.226143" + }, + "homo_sapiens - paired_end - starfusion - spl_junc_tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.SJ.out.tab:md5,19c3faa1bfa9a0cc5e4c45f17065b53a" + ] + ] + ], + "timestamp": "2023-11-23T13:27:56.337072" + }, + "homo_sapiens - single_end - log_out": { + "content": [ + "test.Log.out" + ], + "timestamp": "2023-11-23T13:22:55.126286" + }, + "homo_sapiens - paired_end - log_final": { + "content": [ + "test.Log.final.out" + ], + "timestamp": "2023-11-23T13:23:33.253884" + }, + "homo_sapiens - single_end - log_final": { + "content": [ + "test.Log.final.out" + ], + "timestamp": "2023-11-23T13:22:55.11799" + }, + "homo_sapiens - paired_end - bam_transcript": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:23:33.287684" + }, + "homo_sapiens - paired_end - starfusion - log_progress": { + "content": [ + "test.Log.progress.out" + ], + "timestamp": "2023-11-23T13:27:55.971484" + }, + "homo_sapiens - paired_end - multiple - bam_transcript": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:29:01.264176" + }, + "homo_sapiens - paired_end - multiple - read_per_gene_tab": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:29:01.596406" + }, + "homo_sapiens - single_end - read_per_gene_tab": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:22:55.205936" + }, + "homo_sapiens - paired_end - junction": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:23:33.340653" + }, + "homo_sapiens - paired_end - spl_junc_tab": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.SJ.out.tab:md5,844af19ab0fc8cd9a3f75228445aca0d" + ] + ] + ], + "timestamp": "2023-11-23T13:23:33.398603" + }, + "homo_sapiens - paired_end - starfusion - sam": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:27:56.300637" + }, + "homo_sapiens - paired_end - arriba - bam": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.Aligned.out.bam:md5,c1b1747f5873f2d17762725636e891d5" + ] + ] + ], + "timestamp": "2023-11-23T13:25:06.887604" + }, + "homo_sapiens - single_end - log_progress": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.Log.progress.out:md5,b2bd061d6cbaaf3d6d3b1fed547f69b8" + ] + ] + ], + "timestamp": "2023-11-23T13:22:55.195544" + }, + "homo_sapiens - paired_end - starfusion - wig": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:27:56.422018" + }, + "homo_sapiens - paired_end - wig": { + "content": [ + [ + + ] + ], + "timestamp": "2023-11-23T13:23:33.429457" + }, + "homo_sapiens - paired_end - starfusion - log_out": { + "content": [ + "test.Log.out" + ], + "timestamp": "2023-11-23T13:27:55.93945" + } +} \ No newline at end of file diff --git a/modules/nf-core/star/align/tests/nextflow.arriba.config b/modules/nf-core/star/align/tests/nextflow.arriba.config new file mode 100644 index 00000000..2324b9e5 --- /dev/null +++ b/modules/nf-core/star/align/tests/nextflow.arriba.config @@ -0,0 +1,14 @@ +process { + + withName: STAR_GENOMEGENERATE { + ext.args = '--genomeSAindexNbases 9' + } + + withName: STAR_ALIGN { + ext.args = '--readFilesCommand zcat --outSAMtype BAM Unsorted --outSAMunmapped Within --outBAMcompression 0 --outFilterMultimapNmax 50 --peOverlapNbasesMin 10 --alignSplicedMateMapLminOverLmate 0.5 --alignSJstitchMismatchNmax 5 -1 5 5 --chimSegmentMin 10 --chimOutType WithinBAM HardClip --chimJunctionOverhangMin 10 --chimScoreDropMax 30 --chimScoreJunctionNonGTAG 0 --chimScoreSeparation 1 --chimSegmentReadGapMax 3 --chimMultimapNmax 50' + } + +} + +// Fix chown issue for the output star folder +docker.runOptions = '--platform=linux/amd64 -u $(id -u):$(id -g)' diff --git a/modules/nf-core/star/align/tests/nextflow.config b/modules/nf-core/star/align/tests/nextflow.config new file mode 100644 index 00000000..c4ac5808 --- /dev/null +++ b/modules/nf-core/star/align/tests/nextflow.config @@ -0,0 +1,14 @@ +process { + + withName: STAR_GENOMEGENERATE { + ext.args = '--genomeSAindexNbases 9' + } + + withName: STAR_ALIGN { + ext.args = '--readFilesCommand zcat --outSAMtype BAM SortedByCoordinate --outWigType bedGraph --outWigStrand Unstranded' + } + +} + +// Fix chown issue for the output star folder +docker.runOptions = '--platform=linux/amd64 -u $(id -u):$(id -g)' diff --git a/modules/nf-core/star/align/tests/nextflow.starfusion.config b/modules/nf-core/star/align/tests/nextflow.starfusion.config new file mode 100644 index 00000000..467b6497 --- /dev/null +++ b/modules/nf-core/star/align/tests/nextflow.starfusion.config @@ -0,0 +1,14 @@ +process { + + withName: STAR_GENOMEGENERATE { + ext.args = '--genomeSAindexNbases 9' + } + + withName: STAR_ALIGN { + ext.args = '--readFilesCommand zcat --outSAMtype BAM Unsorted --outReadsUnmapped None --twopassMode Basic --outSAMstrandField intronMotif --outSAMunmapped Within --chimSegmentMin 12 --chimJunctionOverhangMin 8 --chimOutJunctionFormat 1 --alignSJDBoverhangMin 10 --alignMatesGapMax 100000 --alignIntronMax 100000 --alignSJstitchMismatchNmax 5 -1 5 5 --chimMultimapScoreRange 3 --chimScoreJunctionNonGTAG -4 --chimMultimapNmax 20 --chimNonchimScoreDropMin 10 --peOverlapNbasesMin 12 --peOverlapMMp 0.1 --alignInsertionFlush Right --alignSplicedMateMapLminOverLmate 0 --alignSplicedMateMapLmin 30' + } + +} + +// Fix chown issue for the output star folder +docker.runOptions = '--platform=linux/amd64 -u $(id -u):$(id -g)' diff --git a/modules/nf-core/star/align/tests/tags.yml b/modules/nf-core/star/align/tests/tags.yml new file mode 100644 index 00000000..8beace16 --- /dev/null +++ b/modules/nf-core/star/align/tests/tags.yml @@ -0,0 +1,2 @@ +star/align: + - modules/nf-core/star/align/** diff --git a/modules/nf-core/star/genomegenerate/main.nf b/modules/nf-core/star/genomegenerate/main.nf index 473e62a6..d2061844 100644 --- a/modules/nf-core/star/genomegenerate/main.nf +++ b/modules/nf-core/star/genomegenerate/main.nf @@ -4,8 +4,8 @@ process STAR_GENOMEGENERATE { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:1df389393721fc66f3fd8778ad938ac711951107-0' : - 'biocontainers/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:1df389393721fc66f3fd8778ad938ac711951107-0' }" + 'https://depot.galaxyproject.org/singularity/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:019f262d90511939dce2dca4b7c868fc108f73db-0' : + 'biocontainers/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:019f262d90511939dce2dca4b7c868fc108f73db-0' }" input: tuple val(meta), path(fasta) diff --git a/modules/nf-core/star/genomegenerate/tests/main.nf.test b/modules/nf-core/star/genomegenerate/tests/main.nf.test new file mode 100644 index 00000000..eed82926 --- /dev/null +++ b/modules/nf-core/star/genomegenerate/tests/main.nf.test @@ -0,0 +1,38 @@ +nextflow_process { + + name "Test Process STAR_GENOMEGENERATE" + script "../main.nf" + process "STAR_GENOMEGENERATE" + tag "modules" + tag "modules_nfcore" + tag "star" + tag "star/genomegenerate" + + test("homo_sapiens") { + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test_fasta' ], + [file(params.test_data['homo_sapiens']['genome']['genome_fasta'], checkIfExists: true)] + ]) + input[1] = Channel.of([ + [ id:'test_gtf' ], + [file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(file(process.out.index[0][1]).name).match("index") }, + { assert snapshot(process.out.versions).match("versions") } + ) + } + + } + +} \ No newline at end of file diff --git a/modules/nf-core/star/genomegenerate/tests/main.nf.test.snap b/modules/nf-core/star/genomegenerate/tests/main.nf.test.snap new file mode 100644 index 00000000..bd4e0caa --- /dev/null +++ b/modules/nf-core/star/genomegenerate/tests/main.nf.test.snap @@ -0,0 +1,16 @@ +{ + "versions": { + "content": [ + [ + "versions.yml:md5,9c11319b80fdedc90dadce4e0fb42ded" + ] + ], + "timestamp": "2023-11-23T11:18:14.835118" + }, + "index": { + "content": [ + "star" + ], + "timestamp": "2023-11-23T11:31:47.560528" + } +} \ No newline at end of file diff --git a/modules/nf-core/star/genomegenerate/tests/tags.yml b/modules/nf-core/star/genomegenerate/tests/tags.yml new file mode 100644 index 00000000..79f619bf --- /dev/null +++ b/modules/nf-core/star/genomegenerate/tests/tags.yml @@ -0,0 +1,2 @@ +star/genomegenerate: + - modules/nf-core/star/genomegenerate/** diff --git a/modules/nf-core/stringtie/merge/tests/main.nf.test b/modules/nf-core/stringtie/merge/tests/main.nf.test new file mode 100644 index 00000000..90368134 --- /dev/null +++ b/modules/nf-core/stringtie/merge/tests/main.nf.test @@ -0,0 +1,82 @@ +nextflow_process { + + name "Test Process STRINGTIE_MERGE" + script "../main.nf" + process "STRINGTIE_MERGE" + tag "modules" + tag "modules_nfcore" + tag "stringtie" + tag "stringtie/merge" + + test("homo_sapiens - forward strandedness") { + + setup { + run("STRINGTIE_STRINGTIE") { + script "../../stringtie/main.nf" + process { + """ + input[0] = [ + [ id:'test', strandedness:'forward' ], // meta map + [ file(params.test_data['homo_sapiens']['illumina']['test_paired_end_sorted_bam'], checkIfExists: true) ] + ] + input[1] = file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true) + """ + } + } + + } + + when { + process { + """ + input[0] = STRINGTIE_STRINGTIE.out.transcript_gtf.map { it -> it[1] } + input[1] = file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.gtf).match("fs_gtf") }, + { assert snapshot(process.out.versions).match("fs_versions") } + ) + } + } + + test("homo_sapiens - reverse strandedness") { + + setup { + run("STRINGTIE_STRINGTIE") { + script "../../stringtie/main.nf" + process { + """ + input[0] = [ + [ id:'test', strandedness:'reverse' ], // meta map + [ file(params.test_data['homo_sapiens']['illumina']['test_paired_end_sorted_bam'], checkIfExists: true) ] + ] + input[1] = file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true) + """ + } + } + + } + + when { + process { + """ + input[0] = STRINGTIE_STRINGTIE.out.transcript_gtf.map { it -> it[1] } + input[1] = file(params.test_data['homo_sapiens']['genome']['genome_gtf'], checkIfExists: true) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.gtf).match("rs_gtf") }, + { assert snapshot(process.out.versions).match("rs_versions") } + ) + } + } +} diff --git a/modules/nf-core/stringtie/merge/tests/main.nf.test.snap b/modules/nf-core/stringtie/merge/tests/main.nf.test.snap new file mode 100644 index 00000000..3e4bc68f --- /dev/null +++ b/modules/nf-core/stringtie/merge/tests/main.nf.test.snap @@ -0,0 +1,34 @@ +{ + "rs_versions": { + "content": [ + [ + "versions.yml:md5,b73d45fdebf4c8c446bb01817db1665d" + ] + ], + "timestamp": "2023-11-23T14:14:39.697712988" + }, + "rs_gtf": { + "content": [ + [ + "stringtie.merged.gtf:md5,6da479298d73d5b3216d4e1576a2bdf4" + ] + ], + "timestamp": "2023-11-23T14:14:39.691894799" + }, + "fs_gtf": { + "content": [ + [ + "stringtie.merged.gtf:md5,d959eb2fab0db48ded7275e0a2e83c05" + ] + ], + "timestamp": "2023-11-23T14:14:20.872841278" + }, + "fs_versions": { + "content": [ + [ + "versions.yml:md5,b73d45fdebf4c8c446bb01817db1665d" + ] + ], + "timestamp": "2023-11-23T14:14:20.883140097" + } +} \ No newline at end of file diff --git a/modules/nf-core/stringtie/merge/tests/tags.yml b/modules/nf-core/stringtie/merge/tests/tags.yml new file mode 100644 index 00000000..58cef46b --- /dev/null +++ b/modules/nf-core/stringtie/merge/tests/tags.yml @@ -0,0 +1,2 @@ +stringtie/merge: + - modules/nf-core/stringtie/merge/** diff --git a/modules/nf-core/stringtie/stringtie/tests/main.nf.test b/modules/nf-core/stringtie/stringtie/tests/main.nf.test new file mode 100644 index 00000000..68786b74 --- /dev/null +++ b/modules/nf-core/stringtie/stringtie/tests/main.nf.test @@ -0,0 +1,108 @@ +nextflow_process { + + name "Test Process STRINGTIE_STRINGTIE" + script "../main.nf" + process "STRINGTIE_STRINGTIE" + tag "modules" + tag "modules_nfcore" + tag "stringtie" + tag "stringtie/stringtie" + + test("sarscov2 [bam] - forward strandedness") { + + when { + process { + """ + input[0] = [ + [ id:'test', strandedness:'forward' ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_paired_end_sorted_bam'], checkIfExists: true) ] + ] + input[1] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.transcript_gtf).match("fs_transcript_gtf") }, + { assert snapshot(process.out.abundance).match("fs_abundance") }, + { assert snapshot(process.out.versions).match("fs_versions") } + ) + } + } + + test("sarscov2 [bam] - forward strandedness + reference annotation") { + + when { + process { + """ + input[0] = [ + [ id:'test', strandedness:'forward' ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_paired_end_sorted_bam'], checkIfExists: true) ] + ] + input[1] = file(params.test_data['sarscov2']['genome']['genome_gtf'], checkIfExists: true) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.transcript_gtf).match("fs_gtf_transcript_gtf") }, + { assert snapshot(process.out.abundance).match("fs_gtf_abundance") }, + { assert snapshot(process.out.ballgown).match("fs_gtf_ballgown") }, + { assert snapshot(process.out.versions).match("fs_gtf_versions") } + ) + } + } + + test("sarscov2 [bam] - reverse strandedness") { + + when { + process { + """ + input[0] = [ + [ id:'test', strandedness:'reverse' ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_paired_end_sorted_bam'], checkIfExists: true) ] + ] + input[1] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.transcript_gtf).match("rs_transcript_gtf") }, + { assert snapshot(process.out.abundance).match("rs_abundance") }, + { assert snapshot(process.out.versions).match("rs_versions") } + ) + } + } + + test("sarscov2 [bam] - reverse strandedness + reference annotation") { + + when { + process { + """ + input[0] = [ + [ id:'test', strandedness:'reverse' ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_paired_end_sorted_bam'], checkIfExists: true) ] + ] + input[1] = file(params.test_data['sarscov2']['genome']['genome_gtf'], checkIfExists: true) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.transcript_gtf).match("rs_gtf_transcript_gtf") }, + { assert snapshot(process.out.abundance).match("rs_gtf_abundance") }, + { assert snapshot(process.out.ballgown).match("rs_gtf_ballgown") }, + { assert snapshot(process.out.versions).match("rs_gtf_versions") } + ) + } + } +} diff --git a/modules/nf-core/stringtie/stringtie/tests/main.nf.test.snap b/modules/nf-core/stringtie/stringtie/tests/main.nf.test.snap new file mode 100644 index 00000000..bf751636 --- /dev/null +++ b/modules/nf-core/stringtie/stringtie/tests/main.nf.test.snap @@ -0,0 +1,186 @@ +{ + "fs_abundance": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "forward" + }, + "test.gene.abundance.txt:md5,d6f5c8cadb8458f1df0427cf790246e3" + ] + ] + ], + "timestamp": "2023-11-23T13:55:41.032044613" + }, + "fs_transcript_gtf": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "forward" + }, + "test.transcripts.gtf:md5,569137af5be452413086b50653a97203" + ] + ] + ], + "timestamp": "2023-11-23T13:55:41.017978904" + }, + "rs_abundance": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "reverse" + }, + "test.gene.abundance.txt:md5,d6f5c8cadb8458f1df0427cf790246e3" + ] + ] + ], + "timestamp": "2023-11-23T13:56:13.601112933" + }, + "fs_gtf_versions": { + "content": [ + [ + "versions.yml:md5,3410e8ac349d18c85ddee89337851d38" + ] + ], + "timestamp": "2023-11-23T13:56:00.523797974" + }, + "fs_gtf_transcript_gtf": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "forward" + }, + "test.transcripts.gtf:md5,f56cf8aba2c4a5673bc7963ba5f12d04" + ] + ] + ], + "timestamp": "2023-11-23T13:56:00.475164879" + }, + "rs_versions": { + "content": [ + [ + "versions.yml:md5,3410e8ac349d18c85ddee89337851d38" + ] + ], + "timestamp": "2023-11-23T13:56:13.623892691" + }, + "rs_gtf_transcript_gtf": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "reverse" + }, + "test.transcripts.gtf:md5,bb346053a8c156b803b055133376c7fa" + ] + ] + ], + "timestamp": "2023-11-23T13:56:22.693599559" + }, + "fs_gtf_abundance": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "forward" + }, + "test.gene.abundance.txt:md5,7d8bce7f2a922e367cedccae7267c22e" + ] + ] + ], + "timestamp": "2023-11-23T13:56:00.482135418" + }, + "rs_gtf_ballgown": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "reverse" + }, + [ + "e2t.ctab:md5,e981c0038295ae54b63cedb1083f1540", + "e_data.ctab:md5,879b6696029d19c4737b562e9d149218", + "i2t.ctab:md5,8a117c8aa4334b4c2d4711932b006fb4", + "i_data.ctab:md5,be3abe09740603213f83d50dcf81427f", + "t_data.ctab:md5,3b66c065da73ae0dd41cc332eff6a818" + ] + ] + ] + ], + "timestamp": "2023-11-23T13:56:22.715698347" + }, + "rs_transcript_gtf": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "reverse" + }, + "test.transcripts.gtf:md5,31c34aec2bf36bb0ea3c16c2afeeeb1f" + ] + ] + ], + "timestamp": "2023-11-23T13:56:13.590054035" + }, + "rs_gtf_versions": { + "content": [ + [ + "versions.yml:md5,3410e8ac349d18c85ddee89337851d38" + ] + ], + "timestamp": "2023-11-23T13:56:22.725513476" + }, + "fs_gtf_ballgown": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "forward" + }, + [ + "e2t.ctab:md5,e981c0038295ae54b63cedb1083f1540", + "e_data.ctab:md5,6b4cf69bc03f3f69890f972a0e8b7471", + "i2t.ctab:md5,8a117c8aa4334b4c2d4711932b006fb4", + "i_data.ctab:md5,be3abe09740603213f83d50dcf81427f", + "t_data.ctab:md5,3b66c065da73ae0dd41cc332eff6a818" + ] + ] + ] + ], + "timestamp": "2023-11-23T13:56:00.494299817" + }, + "fs_versions": { + "content": [ + [ + "versions.yml:md5,3410e8ac349d18c85ddee89337851d38" + ] + ], + "timestamp": "2023-11-23T13:55:41.049417582" + }, + "rs_gtf_abundance": { + "content": [ + [ + [ + { + "id": "test", + "strandedness": "reverse" + }, + "test.gene.abundance.txt:md5,7385b870b955dae2c2ab78a70cf05cce" + ] + ] + ], + "timestamp": "2023-11-23T13:56:22.701059059" + } +} diff --git a/modules/nf-core/stringtie/stringtie/tests/tags.yml b/modules/nf-core/stringtie/stringtie/tests/tags.yml new file mode 100644 index 00000000..da9b051c --- /dev/null +++ b/modules/nf-core/stringtie/stringtie/tests/tags.yml @@ -0,0 +1,2 @@ +stringtie/stringtie: + - modules/nf-core/stringtie/stringtie/** diff --git a/nextflow_schema.json b/nextflow_schema.json index 6dd54f7a..29ae6288 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -295,7 +295,6 @@ "description": "Path to FASTA genome file.", "fa_icon": "far fa-file-code" }, - "fai": { "type": "string", "format": "file-path", From 4ae96ff0f44e1d7fc1350f8a308eff6c4b09fa54 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:38:14 +0100 Subject: [PATCH 175/175] clarify changelog --- CHANGELOG.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c0a4278..75d0ce76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,36 +3,33 @@ 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). -## v3.0.0 - [2023-11-28] +## v3.0.0 - [2023-11-27] ### Added -- Add picard CollectInserSizeMetrics to QC workflow [#408](https://github.com/nf-core/rnafusion/pull/408) -- Build CRAM index in the same directory as CRAM files for arriba and STAR-Fusion [#427](https://github.com/nf-core/rnafusion/pull/427) +- Add picard CollectInsertSizeMetrics to QC workflow [#408](https://github.com/nf-core/rnafusion/pull/408) +- Build CRAM index in the same directory as CRAM files for Arriba and STAR-Fusion [#427](https://github.com/nf-core/rnafusion/pull/427) ### Changed - Replace PICARD_MARKDUPLICATES with GATK4_MARKDUPLICATES [#409](https://github.com/nf-core/rnafusion/pull/409) - Removed `--fusioninspector_filter` and `--fusionreport_filter` in favor of `--tools_cutoff` (default = 1, no filters applied) [#389](https://github.com/nf-core/rnafusion/pull/389) -- Now publishing convert2bed output to convert2bed to keep the output file for mosdepth [#420](https://github.com/nf-core/rnafusion/pull/420) +- Now publishing convert2bed output to convert2bed to keep the output file [#420](https://github.com/nf-core/rnafusion/pull/420) - No more checks for existence of samplesheet, which made building references fail (building references uses a fake sample sheet if none is provided) [#420](https://github.com/nf-core/rnafusion/pull/420) -- `--extreme_sensitivity` used for fusioninspector to minimize fusioninspector filtering [#424](https://github.com/nf-core/rnafusion/pull/424) -- `--extreme_sensitivity` removed in favor of `--max_sensitivity --max_mate_dist 10000000 --annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) +- `--annotate --examine_coding_effect` to collect more data from fusioninspector [#426](https://github.com/nf-core/rnafusion/pull/426) +- Update vcf creation to get positions/chromosomes and strands even when fusions are filtered out by fusioninspector, using the csv output from fusion-report [#443](https://github.com/nf-core/rnafusion/pull/443) - `Arriba` updated to 2.4.0 [#429](https://github.com/nf-core/rnafusion/pull/429) - Change megafusion into vcf_collect, taking into account e.g. the annotation and coding effects outputs from fusioninspector, HGNC ids, frame status... [#414](https://github.com/nf-core/rnafusion/pull/414) - CI tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#430](https://github.com/nf-core/rnafusion/pull/430) - AWS tests on `--all` instead of each tool separately, and include trimmed/not trimmed matrix tests [#433](https://github.com/nf-core/rnafusion/pull/433) -- Update `fusion-report` to 2.1.5p8 to fix download of references via proxy [#432](https://github.com/nf-core/rnafusion/pull/432) -- Remove `--max_sensitivity --max_mate_dist 10000000` from fusioninspector. Update vcf creation to get positions/chromosomes and strands even when fusions are filtered out by fusioninspector, using the csv output from fusion-report [#443](https://github.com/nf-core/rnafusion/pull/443) -- Update `fusion-report` to 2.1.8, removing FusionGDB database [#445](https://github.com/nf-core/rnafusion/pull/445) +- Update `fusion-report` to 2.1.8, updated COSMIC database to fix 404 error, fix download of references via proxy and removing FusionGDB database [#445](https://github.com/nf-core/rnafusion/pull/445) - Update documentation [#446](https://github.com/nf-core/rnafusion/pull/446) ### Fixed - Fix channel i/o issue in StringTie workflow and add StringTie in github CI tests [#416](https://github.com/nf-core/rnafusion/pull/416) -- Updated COSMIC database to fix 404 error while downloading fusionreport references [#420](https://github.com/nf-core/rnafusion/pull/420) - Update modules, and make sure MultiQC displays the QC results properly [#440](https://github.com/nf-core/rnafusion/pull/440) -- Add 'when' condition to run collectinsertsize [#444](https://github.com/nf-core/rnafusion/pull/444) +- Add 'when' condition to run CollectInsertSizeMetrics only when STAR-fusion bam files are available [#444](https://github.com/nf-core/rnafusion/pull/444) ### Removed