From e5cfedb18ca74e2a319dc8edc5521bbf38f86971 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Thu, 25 Sep 2025 07:59:50 +0200 Subject: [PATCH 01/27] fix: update catalog yaml --- .snakemake-workflow-catalog.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.snakemake-workflow-catalog.yml b/.snakemake-workflow-catalog.yml index b524029..a4fe5ac 100644 --- a/.snakemake-workflow-catalog.yml +++ b/.snakemake-workflow-catalog.yml @@ -1,6 +1,11 @@ +# configuration of display in snakemake workflow catalog: https://snakemake.github.io/snakemake-workflow-catalog + usage: - software-stack-deployment: # definition of software deployment method (at least one of conda, singularity, or singularity+conda) - conda: true # whether pipeline works with --use-conda - singularity: false # whether pipeline works with --use-singularity - singularity+conda: false # whether pipeline works with --use-singularity --use-conda - report: true # add this to confirm that the workflow allows to use 'snakemake --report report.zip' to generate a report containing all results and explanations \ No newline at end of file + mandatory-flags: + desc: # describe your flags here in a few sentences + flags: # put your flags here + software-stack-deployment: + conda: true # whether pipeline works with '--sdm conda' + apptainer: false # whether pipeline works with '--sdm apptainer/singularity' + apptainer+conda: false # whether pipeline works with '--sdm conda apptainer/singularity' + report: true # whether creation of reports using 'snakemake --report report.zip' is supported \ No newline at end of file From e147f0568bb17bb8433e53dcad7c54b81b466326 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Thu, 25 Sep 2025 08:05:32 +0200 Subject: [PATCH 02/27] fix: simplified sample paths, removed unlisting --- .../samplesheet/samples_mpusp_custom.tsv | 6 +- .test/config/samplesheet/samples_neb_umi.tsv | 6 +- .test/config/samplesheet/samples_nextflex.tsv | 6 +- config/samplesheet/samples.tsv | 3 - workflow/rules/common.smk | 163 ++++++------------ 5 files changed, 63 insertions(+), 121 deletions(-) delete mode 100644 config/samplesheet/samples.tsv diff --git a/.test/config/samplesheet/samples_mpusp_custom.tsv b/.test/config/samplesheet/samples_mpusp_custom.tsv index f673c92..58750fe 100644 --- a/.test/config/samplesheet/samples_mpusp_custom.tsv +++ b/.test/config/samplesheet/samples_mpusp_custom.tsv @@ -1,3 +1,3 @@ -sample condition replicate experiment data_folder fq1 fq2 fq_umi -RNA-1 RNA 1 rnaseq_mpusp_custom data/rnaseq_mpusp_custom RNA-1_R1.fastq.gz RNA-1_R2.fastq.gz - -RNA-2 RNA 2 rnaseq_mpusp_custom data/rnaseq_mpusp_custom RNA-2_R1.fastq.gz RNA-2_R2.fastq.gz - \ No newline at end of file +sample condition replicate experiment fq1 fq2 fq_umi +RNA-1 RNA 1 rnaseq_mpusp_custom data/rnaseq_mpusp_custom/RNA-1_R1.fastq.gz data/rnaseq_mpusp_custom/RNA-1_R2.fastq.gz - +RNA-2 RNA 2 rnaseq_mpusp_custom data/rnaseq_mpusp_custom/RNA-2_R1.fastq.gz data/rnaseq_mpusp_custom/RNA-2_R2.fastq.gz - diff --git a/.test/config/samplesheet/samples_neb_umi.tsv b/.test/config/samplesheet/samples_neb_umi.tsv index 3270b1d..fcf7ad3 100644 --- a/.test/config/samplesheet/samples_neb_umi.tsv +++ b/.test/config/samplesheet/samples_neb_umi.tsv @@ -1,3 +1,3 @@ -sample condition replicate experiment data_folder fq1 fq2 fq_umi -RNA-1 RNA 1 rnaseq_neb_umi data/rnaseq_neb_umi RNA-1_R1.fastq.gz RNA-1_R2.fastq.gz RNA-1_UMI.fastq.gz -RNA-2 RNA 2 rnaseq_neb_umi data/rnaseq_neb_umi RNA-2_R1.fastq.gz RNA-2_R2.fastq.gz RNA-2_UMI.fastq.gz \ No newline at end of file +sample condition replicate experiment fq1 fq2 fq_umi +RNA-1 RNA 1 rnaseq_neb_umi data/rnaseq_neb_umi/RNA-1_R1.fastq.gz data/rnaseq_neb_umi/RNA-1_R2.fastq.gz data/rnaseq_neb_umi/RNA-1_UMI.fastq.gz +RNA-2 RNA 2 rnaseq_neb_umi data/rnaseq_neb_umi/RNA-2_R1.fastq.gz data/rnaseq_neb_umi/RNA-2_R2.fastq.gz data/rnaseq_neb_umi/RNA-2_UMI.fastq.gz diff --git a/.test/config/samplesheet/samples_nextflex.tsv b/.test/config/samplesheet/samples_nextflex.tsv index c62bc59..a35df67 100644 --- a/.test/config/samplesheet/samples_nextflex.tsv +++ b/.test/config/samplesheet/samples_nextflex.tsv @@ -1,3 +1,3 @@ -sample condition replicate experiment data_folder fq1 fq2 fq_umi -RNA-1 RNA 1 rnaseq_nextflex data/rnaseq_nextflex RNA-1_R1.fastq.gz RNA-1_R2.fastq.gz - -RNA-2 RNA 2 rnaseq_nextflex data/rnaseq_nextflex RNA-2_R1.fastq.gz RNA-2_R2.fastq.gz - \ No newline at end of file +sample condition replicate experiment fq1 fq2 fq_umi +RNA-1 RNA 1 rnaseq_nextflex data/rnaseq_nextflex/RNA-1_R1.fastq.gz RNA-1_R2.fastq.gz - +RNA-2 RNA 2 rnaseq_nextflex data/rnaseq_nextflex/RNA-2_R1.fastq.gz RNA-2_R2.fastq.gz - diff --git a/config/samplesheet/samples.tsv b/config/samplesheet/samples.tsv deleted file mode 100644 index 624f53f..0000000 --- a/config/samplesheet/samples.tsv +++ /dev/null @@ -1,3 +0,0 @@ -sample condition replicate experiment data_folder fq1 fq2 fq_umi -RNA-1 RNA 1 rnaseq_neb_umi .test/data/rnaseq_neb_umi RNA-1_R1.fastq.gz RNA-1_R2.fastq.gz RNA-1_UMI.fastq.gz -RNA-2 RNA 2 rnaseq_neb_umi .test/data/rnaseq_neb_umi RNA-2_R1.fastq.gz RNA-2_R2.fastq.gz RNA-2_UMI.fastq.gz \ No newline at end of file diff --git a/workflow/rules/common.smk b/workflow/rules/common.smk index fb79727..a765581 100644 --- a/workflow/rules/common.smk +++ b/workflow/rules/common.smk @@ -1,4 +1,3 @@ -import itertools import os import pandas as pd import yaml @@ -97,40 +96,23 @@ def get_qc_input(wildcards): """Get QC input file.""" inputs = [] if wildcards.status == "raw": - inputs.append( - expand( - "{input_dir}/{sample}", - input_dir=samples.loc[wildcards.sample]["data_folder"], - sample=samples.loc[wildcards.sample]["fq1"], - ) - ) + inputs += [samples.loc[wildcards.sample]["fq1"]] if not is_single_end(wildcards.sample): # append R2 if sample is paired-end - inputs.append( - expand( - "{input_dir}/{sample}", - input_dir=samples.loc[wildcards.sample]["data_folder"], - sample=samples.loc[wildcards.sample]["fq2"], - ) - ) - + inputs += [samples.loc[wildcards.sample]["fq2"]] if wildcards.status == "clipped": if not is_single_end(wildcards.sample): - inputs.append( - expand( - os.path.join("results", "clipped", "{sample}_{read}.fastq.gz"), - sample=wildcards.sample, - read=["R1", "R2"], - ) + inputs += expand( + "results/umi_extract/{sample}_{read}.fastq.gz", + sample=wildcards.sample, + read=["R1", "R2"], ) else: - inputs.append( - expand( - os.path.join("results", "clipped", "{sample}.fastq.gz"), - sample=wildcards.sample, - ) + inputs += expand( + "results/clipped/{sample}.fastq.gz", + sample=wildcards.sample, ) - return list(itertools.chain.from_iterable(inputs)) + return inputs # get fastq files for umi extract input @@ -138,32 +120,14 @@ def get_umi_input(wildcards): """Get FASTQ files UMI extraction.""" inputs = [] # three fastq files - inputs.append( - expand( - "{input_dir}/{sample}", - input_dir=samples.loc[wildcards.sample]["data_folder"], - sample=samples.loc[wildcards.sample]["fq1"], - ) - ) + inputs.append(samples.loc[wildcards.sample]["fq1"]) if not is_single_end(wildcards.sample): # append R2 if sample is paired-end - inputs.append( - expand( - "{input_dir}/{sample}", - input_dir=samples.loc[wildcards.sample]["data_folder"], - sample=samples.loc[wildcards.sample]["fq2"], - ) - ) + inputs.append(samples.loc[wildcards.sample]["fq2"]) # include third fastq file if is_rnaseq_neb_umi: - inputs.append( - expand( - "{input_dir}/{sample}", - input_dir=samples.loc[wildcards.sample]["data_folder"], - sample=samples.loc[wildcards.sample]["fq_umi"], - ) - ) - return list(itertools.chain.from_iterable(inputs)) + inputs.append(samples.loc[wildcards.sample]["fq_umi"]) + return inputs # returns fastq files for trimming @@ -171,18 +135,14 @@ def get_trimming_input(wildcards): """Get FASTQ files for trimming.""" inputs = [] if is_single_end_experiment: - inputs.append( - expand("results/umi_extract/{sample}.fastq.gz", sample=wildcards.sample) - ) + inputs += expand("results/umi_extract/{sample}.fastq.gz", sample=wildcards.sample) elif is_paired_end_experiment: - inputs.append( - expand( - "results/umi_extract/{sample}_{read}.fastq.gz", - sample=wildcards.sample, - read=["R1", "R2"], - ) + inputs += expand( + "results/umi_extract/{sample}_{read}.fastq.gz", + sample=wildcards.sample, + read=["R1", "R2"], ) - return list(itertools.chain.from_iterable(inputs)) + return inputs # returns fastq files for truncation post trimming @@ -190,16 +150,14 @@ def get_trunc_input(wildcards): """Get FASTQ files for trunction post trimming.""" inputs = [] if is_single_end_experiment: - inputs.append(expand("results/clipped/{sample}.fastq.gz", sample=wildcards.sample)) + inputs += expand("results/clipped/{sample}.fastq.gz", sample=wildcards.sample) elif is_paired_end_experiment: - inputs.append( - expand( - "results/clipped/{sample}_{read}.fastq.gz", - sample=wildcards.sample, - read=["R1", "R2"], - ) + inputs += expand( + "results/clipped/{sample}_{read}.fastq.gz", + sample=wildcards.sample, + read=["R1", "R2"], ) - return list(itertools.chain.from_iterable(inputs)) + return inputs # returns fastq files for mapping @@ -207,28 +165,22 @@ def get_mapping_input(wildcards): """Get FASTQ files for trimming.""" inputs = [] if is_single_end_experiment and (is_rnaseq_mpusp_custom or is_rnaseq_neb_umi): - inputs.append(expand("results/clipped/{sample}.fastq.gz", sample=wildcards.sample)) + inputs += expand("results/clipped/{sample}.fastq.gz", sample=wildcards.sample) elif is_single_end_experiment and is_rnaseq_nextflex: - inputs.append( - expand("results/trunc_fastq/{sample}.fastq.gz", sample=wildcards.sample) - ) + inputs += expand("results/trunc_fastq/{sample}.fastq.gz", sample=wildcards.sample) elif is_paired_end_experiment and (is_rnaseq_mpusp_custom or is_rnaseq_neb_umi): - inputs.append( - expand( - "results/clipped/{sample}_{read}.fastq.gz", - sample=wildcards.sample, - read=["R1", "R2"], - ) + inputs += expand( + "results/clipped/{sample}_{read}.fastq.gz", + sample=wildcards.sample, + read=["R1", "R2"], ) elif is_paired_end_experiment and is_rnaseq_nextflex: - inputs.append( - expand( - "results/trunc_fastq/{sample}_{read}.fastq.gz", - sample=wildcards.sample, - read=["R1", "R2"], - ) + inputs += expand( + "results/trunc_fastq/{sample}_{read}.fastq.gz", + sample=wildcards.sample, + read=["R1", "R2"], ) - return list(itertools.chain.from_iterable(inputs)) + return inputs # return bam files for alignment qc @@ -264,40 +216,33 @@ def get_conda_envs_files(): wf_dir = os.path.abspath(workflow.basedir) envs = [] try: - envs.append( - expand( - os.path.join(wf_dir, "envs", "{envs}"), envs=os.listdir(f"{wf_dir}/envs") - ) + envs += expand( + os.path.join(wf_dir, "envs", "{envs}"), envs=os.listdir(f"{wf_dir}/envs") ) except FileNotFoundError: - envs.append([""]) - return list(itertools.chain.from_iterable(envs)) + msg = "No conda environments found in the 'envs' directory." + logger.error(msg) + return envs def construct_multiqc_input(): inputs = [] - inputs.append( - expand( - "results/qc/{status}_reads/{sample}", - status=["raw", "clipped"], - sample=samples.index, - ), + inputs += expand( + "results/qc/{status}_reads/{sample}", + status=["raw", "clipped"], + sample=samples.index, ) - inputs.append( - expand( - "results/qc/{step}_alignment/{sample}.flagstat", - step=["mapped", "dedup"], - sample=samples.index, - ) + inputs += expand( + "results/qc/{step}_alignment/{sample}.flagstat", + step=["mapped", "dedup"], + sample=samples.index, ) - inputs.append( - expand( - "results/qc/biotypes/{sample}.counts.summary", - sample=samples.index, - ) + inputs += expand( + "results/qc/biotypes/{sample}.counts.summary", + sample=samples.index, ) inputs.append(["results/qc/biotypes/barplot_biotype_data_mqc.json"]) - return list(itertools.chain.from_iterable(inputs)) + return inputs def define_multiqc_dirs(): From b5fdbe16f5753f220aeaa24aa81bc8157ed7c9c7 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Thu, 25 Sep 2025 08:31:56 +0200 Subject: [PATCH 03/27] feat: split READMEs according to catalog standard --- README.md | 125 +++++++++++++---------------------------------- config/README.md | 59 ++++++++++++++++++++++ 2 files changed, 94 insertions(+), 90 deletions(-) create mode 100644 config/README.md diff --git a/README.md b/README.md index 6cd372b..0d72ea1 100644 --- a/README.md +++ b/README.md @@ -6,26 +6,24 @@ [![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) [![workflow catalog](https://img.shields.io/badge/Snakemake%20workflow%20catalog-darkgreen)](https://snakemake.github.io/snakemake-workflow-catalog) ------------------------------------------------------------------------- +--- A Snakemake workflow for the processing of short read rnaseq data in bacteria. -- [Bacterial RNAseq processing](#anchortitle) - - [Usage](#usage) - - [Workflow overview](#workflow-overview) - - [Installation](#installation) - - [Running the workflow](#running-the-workflow) - - [Input data](#input-data) - - [Reference genome](#reference-genome) - - [Execution](#execution) - - [Authors](#authors) - - [References](#references) +- [Snakemake workflow: bacterial-rnaseq-processing](#snakemake-workflow-bacterial-rnaseq-processing) + - [Usage](#usage) + - [Workflow overview](#workflow-overview) + - [Deployment options](#deployment-options) + - [Authors](#authors) + - [References](#references) ## Usage -The usage of this workflow is described in the [Snakemake Workflow Catalog](https://snakemake.github.io/snakemake-workflow-catalog/?usage=MPUSP%2Fsnakemake-bacterial-rnaseq-processing). +The usage of this workflow is described in the [Snakemake Workflow Catalog](https://snakemake.github.io/snakemake-workflow-catalog/docs/workflows/MPUSP/snakemake-bacterial-rnaseq-processing). + +Detailed information about input data and workflow configuration can also be found in the [`config/README.md`](config/README.md). -If you use this workflow in a paper, don't forget to give credits to the authors by citing the URL of this (original) sitory and its DOI (see above). +If you use this workflow in a paper, don't forget to give credits to the authors by citing the URL of this repository or its DOI. ## Workflow overview @@ -47,101 +45,48 @@ This workflow is a best-practice workflow for the processing of short read seque --- ![](resources/images/dag.png) -

Figure 1: Directed acyclic graph (DAG) of the current workflow steps.

- -## Installation - -**Step 1: Clone this repository** - -``` bash -git clone https://github.com/MPUSP/snakemake-bacterial-rnaseq-processing.git -cd snakemake-bacterial-rnaseq-processing -``` - -**Step 2: Install dependencies** -It is recommended to install snakemake and run the workflow with `conda` or `mamba`. [Miniforge](https://conda-forge.org/download/) is the preferred conda-forge installer and includes `conda`, `mamba` and their dependencies. - -**Step 3: Create snakemake environment** - -This step creates a new conda environment called `snakemake-bacterial-rnaseq-processing`. - -``` bash -# create new environment with dependencies & activate it -mamba create -c conda-forge -c bioconda -n snakemake-bacterial-rnaseq-processing snakemake pandas python=3.12 -conda activate snakemake-bacterial-rnaseq-processing -``` - -**Note:** - -All other dependencies for the workflow are **automatically pulled as `conda` environments** by snakemake, when running the workflow with the `--sdm-conda` parameter (recommended). +

Figure 1: Directed acyclic graph (DAG) of the current workflow steps.

-**Step 4: Create all rule specific environments (optional)** +## Deployment options -This step creates all conda environments specified in the snakemake rules. This step is optional. +To run the workflow from command line, change the working directory. -``` bash -# activate new environment -conda activate snakemake-bacterial-rnaseq-processing -snakemake -c 1 --sdm conda --conda-create-envs-only --conda-cleanup-pkgs cache +```bash +cd path/to/snakemake-workflow-name ``` -## Running the workflow - -### Input data - -#### Reference genome - -An NCBI Refseq ID, e.g. `GCF_000006785.2`. Find your genome assembly and corresponding ID on [NCBI genomes](https://www.ncbi.nlm.nih.gov/data-hub/genome/). Alternatively use a custom pair of `*.fasta` file and `*.gff` file that describe the genome of choice. - -Important requirements when using custom `*.fasta` and `*.gff` files: - -- `*.gff` genome annotation must have the same chromosome/region name as the `*.fasta` file (example: `NC_002737.2`) -- `*.gff` genome annotation must have `gene` and `CDS` type annotation that is automatically parsed to extract transcripts -- all chromosomes/regions in the `*.gff` genome annotation must be present in the `*.fasta` sequence -- but not all sequences in the `*.fasta` file need to have annotated genes in the `*.gff` file - -#### Read data - -RNA sequencing data in `*.fastq.gz` format. The currently supported input data are **second generation reads**. Input data files are supplied via a mandatory table, whose location is indicated in the `config.yml` file (default: `samples.tsv`). The sample sheet has the following layout: - -| sample | condition | replicate | experiment | data_folder | fq1 | fq2 | fq_umi | -|---------|---------|---------|---------|---------|---------|---------|---------| -| RNA-1 | RNA | 1 | rnaseq_mpusp_custom | data | RNA-1_R1.fastq.gz | RNA-1_R2.fastq.gz | \- | -| RNA-2 | RNA | 2 | rnaseq_mpusp_custom | data | RNA-2_R2.fastq.gz | RNA-2_R2.fastq.gz | \- | - -Some configuration parameters of the pipeline may be specific for your data and library preparation protocol. The options should be adjusted in the `config.yml` file. - -Currently, we support example configurations for three different sequencing protocols, *i.e.* `rnaseq_nextflex`, `rnaseq_neb_umi`and `rnseq_mpusp_custom`. These example protocols can be found in `resources/protocols/`. - -### Execution - -To run the workflow from command line, change the working directory. +Adjust options in the default config file `config/config.yml`. +Before running the complete workflow, you can perform a dry run using: -``` bash -cd snakemake-bacterial-rnaseq-processing +```bash +snakemake --dry-run ``` -To run the complete workflow with test files using **`conda`**, execute the following command. The definition of the number of compute cores is mandatory. +To run the workflow with test files using **conda**: -``` bash -snakemake --cores 10 --sdm conda --directory .test +```bash +snakemake --cores 2 --sdm conda --directory .test ``` -To run the workflow with your own data, define the sample sheet as explained above and adjust options in the default config file `config/config.yml` according to your library preparation protocol. Before running the entire workflow, you can perform a dry run using: +To run the workflow with **apptainer** / **singularity** (not yet supported): -``` bash -snakemake -c 1 --sdm conda --dry-run +```bash +snakemake --cores 2 --sdm conda apptainer --directory .test ``` -## Author +## Authors -- Dr. Rina Ahmed-Begrich - - Affiliation: [Max-Planck-Unit for the Science of Pathogens](https://www.mpusp.mpg.de/) (MPUSP), Berlin, Germany - - ORCID profile: https://orcid.org/0000-0002-0656-1795 +- Dr Rina Ahmed-Begrich + - Affiliation: [Max-Planck-Unit for the Science of Pathogens](https://www.mpusp.mpg.de/) (MPUSP), Berlin, Germany + - ORCID profile: https://orcid.org/0000-0002-0656-1795 +- Dr. Michael Jahn + - Affiliation: [Max-Planck-Unit for the Science of Pathogens](https://www.mpusp.mpg.de/) (MPUSP), Berlin, Germany + - ORCID profile: https://orcid.org/0000-0002-3913-153X + - github page: https://github.com/m-jahn Visit the MPUSP github page at https://github.com/MPUSP for more info on this workflow and other projects. ## References -- Essential tools are linked in the top section of this document \ No newline at end of file +- Essential tools are linked in the top section of this document diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000..60f77f7 --- /dev/null +++ b/config/README.md @@ -0,0 +1,59 @@ +## Workflow overview + +This workflow is a best-practice workflow for the processing of short read sequencing data in bacteria. The workflow is built using [snakemake](https://snakemake.readthedocs.io/en/stable/) and consists of the following steps: + +1. Obtain genome database in `fasta` and `gff` format (`python`, [NCBI Datasets](https://www.ncbi.nlm.nih.gov/datasets/docs/v2/)) + 1. Using automatic download from NCBI with a `RefSeq` ID + 2. Using user-supplied files +2. Check quality of input sequencing data ([FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) +3. Cut adapters and filter by length and/or sequencing quality score ([Cutadapt](https://cutadapt.readthedocs.io/en/stable/)) +4. Identify unique molecular identifier (UMI, [UMI-tools](https://umi-tools.readthedocs.io/en/latest/)) +5. Map reads to the reference genome ([STAR aligner](https://github.com/alexdobin/STAR)) +6. Sort and index aligned rnaseq data ([Samtools](http://www.htslib.org/)) +7. Deduplicate reads by unique molecular identifier (UMI, [UMI-tools](https://umi-tools.readthedocs.io/en/latest/)) +8. Generate cpm normalized coverage files ([deepTools](https://deeptools.readthedocs.io/en/latest/)) +9. Quantify biotype features ([featureCounts](https://subread.sourceforge.net/featureCounts.html)) +10. Generate summary report for all processing steps ([MultiQC](https://seqera.io/multiqc/)) + +## Running the workflow + +### Input + +#### Reference genome + +An NCBI Refseq ID, e.g. `GCF_000006785.2`. Find your genome assembly and corresponding ID on [NCBI genomes](https://www.ncbi.nlm.nih.gov/data-hub/genome/). Alternatively use a custom pair of `*.fasta` file and `*.gff` file that describe the genome of choice. + +Important requirements when using custom `*.fasta` and `*.gff` files: + +- `*.gff` genome annotation must have the same chromosome/region name as the `*.fasta` file (example: `NC_002737.2`) +- `*.gff` genome annotation must have `gene` and `CDS` type annotation that is automatically parsed to extract transcripts +- all chromosomes/regions in the `*.gff` genome annotation must be present in the `*.fasta` sequence +- but not all sequences in the `*.fasta` file need to have annotated genes in the `*.gff` file + +#### Read data + +RNA sequencing data in `*.fastq.gz` format. The currently supported input data are **second generation reads**. Input data files are supplied via a mandatory table, whose location is indicated in the `config.yml` file (default: `samples.tsv`). The sample sheet has the following layout: + +| sample | condition | replicate | experiment | fq1 | fq2 | fq_umi | +| ------ | --------- | --------- | ------------------- | ----------------- | ----------------- | ------ | +| RNA-1 | RNA | 1 | rnaseq_mpusp_custom | RNA-1_R1.fastq.gz | RNA-1_R2.fastq.gz | \- | +| RNA-2 | RNA | 2 | rnaseq_mpusp_custom | RNA-2_R2.fastq.gz | RNA-2_R2.fastq.gz | \- | + +Some configuration parameters of the pipeline may be specific for your data and library preparation protocol. The options should be adjusted in the `config.yml` file. + +Currently, we support example configurations for three different sequencing protocols, _i.e._ `rnaseq_nextflex`, `rnaseq_neb_umi`and `rnseq_mpusp_custom`. These example protocols can be found in `resources/protocols/`. + +### Output + +### Output + +| Output File/Folder | Description | +| ---------------------------- | ------------------------------------------------------------------------ | +| `results/clipped/` | Adapter-trimmed and quality-filtered FASTQ files. | +| `results/deduplicated/` | UMI-processed FASTQ/BAM files and UMI statistics. | +| `results/genome/` | Downloaded or user-supplied reference genome and annotation files. | +| `results/mapped/` | Aligned reads in BAM format (sorted and indexed). | +| `results/qc/` | Quality control reports for raw and processed reads (FastQC HTML files). | +| `results/deeptools/` | CPM-normalized coverage files (bigWig format). | +| `results/quantify_biotypes/` | Gene/feature count tables (tab-delimited text files). | +| `results/report/` | MultiQC report aggregating QC metrics from all steps. | From ea4a4ea36cd3fbb8aedf2ffe2d9ac1bd9d54a707 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Thu, 25 Sep 2025 08:32:30 +0200 Subject: [PATCH 04/27] fix: remove style config as it deviates from prettier standard --- .editorconfig | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 5573fb6..0000000 --- a/.editorconfig +++ /dev/null @@ -1,15 +0,0 @@ -# EditorConfig is awesome: http://EditorConfig.org - -# top-most EditorConfig file -root = true - -[*] -end_of_line = lf -insert_final_newline = true -charset = utf-8 -indent_style = space -indent_size = 4 - -[*.{yml,yaml}] -indent_style = space -indent_size = 2 From e38c83f6c5f2500bd1ee6a17ab59afa301a07f9a Mon Sep 17 00:00:00 2001 From: m-jahn Date: Thu, 25 Sep 2025 08:39:08 +0200 Subject: [PATCH 05/27] fix: update GH actions wfs --- .github/workflows/main.yml | 16 +++++++--------- .github/workflows/release-please.yml | 3 ++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b238a06..849c821 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,14 +2,13 @@ name: Tests on: push: - branches: [main, dev] + branches: [main] pull_request: - branches: [main, dev] + branches: [main] jobs: Formatting: runs-on: ubuntu-latest - if: ${{ github.actor != 'github-actions[bot]' }} steps: - uses: actions/checkout@v4 with: @@ -25,11 +24,10 @@ jobs: Linting: runs-on: ubuntu-latest - if: ${{ github.actor != 'github-actions[bot]' }} steps: - uses: actions/checkout@v4 - name: Lint workflow - uses: snakemake/snakemake-github-action@v1.25.1 + uses: snakemake/snakemake-github-action@v2 with: directory: . snakefile: workflow/Snakefile @@ -37,21 +35,21 @@ jobs: Testing: runs-on: ubuntu-latest - if: ${{ github.actor != 'github-actions[bot]' }} needs: - Linting - Formatting steps: - uses: actions/checkout@v4 + - name: Test workflow - uses: snakemake/snakemake-github-action@v1.25.1 + uses: snakemake/snakemake-github-action@v2 with: directory: .test snakefile: workflow/Snakefile - args: "--use-conda --show-failed-logs --cores 3 --conda-cleanup-pkgs cache" + args: "--sdm conda --show-failed-logs --cores 2 --conda-cleanup-pkgs cache --all-temp" - name: Test report - uses: snakemake/snakemake-github-action@v1.25.1 + uses: snakemake/snakemake-github-action@v2 with: directory: .test snakefile: workflow/Snakefile diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 7643ac7..e9ec84c 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -6,6 +6,7 @@ on: permissions: contents: write pull-requests: write + issues: write name: release-please @@ -16,4 +17,4 @@ jobs: - uses: googleapis/release-please-action@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - release-type: simple + release-type: go # just keep a changelog, no version anywhere outside of git tags From f8b5da4e49e7d6005eb7e8fce79ccc59dfa19ad9 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Wed, 1 Oct 2025 15:05:59 +0200 Subject: [PATCH 06/27] feat: major refactoring of logic and replacement of custom rules with wrappers --- .test/config/config.yml | 55 ++-- .../samplesheet/samples_mpusp_custom.tsv | 6 +- .test/config/samplesheet/samples_neb_umi.tsv | 2 +- .test/config/samplesheet/samples_nextflex.tsv | 2 +- config/README.md | 2 +- workflow/Snakefile | 2 +- workflow/envs/cutadapt.yml | 6 - workflow/envs/deeptools.yml | 6 - workflow/envs/fastqc.yml | 6 - workflow/envs/get_genome.yml | 10 - workflow/envs/multiqc.yml | 6 - workflow/envs/samtools.yml | 7 - workflow/envs/star.yml | 8 - workflow/rules/common.smk | 251 +++++------------ workflow/rules/coverage.smk | 25 ++ workflow/rules/coverage_tracks.smk | 48 ---- workflow/rules/dedup.smk | 253 +++++++----------- workflow/rules/mapping.smk | 144 +++++----- workflow/rules/qc.smk | 133 +++------ workflow/rules/quantification.smk | 115 +++----- workflow/rules/trim.smk | 137 ++-------- workflow/scripts/get_genome.py | 163 ----------- 22 files changed, 359 insertions(+), 1028 deletions(-) delete mode 100644 workflow/envs/cutadapt.yml delete mode 100644 workflow/envs/deeptools.yml delete mode 100644 workflow/envs/fastqc.yml delete mode 100644 workflow/envs/get_genome.yml delete mode 100644 workflow/envs/multiqc.yml delete mode 100644 workflow/envs/samtools.yml delete mode 100644 workflow/envs/star.yml create mode 100644 workflow/rules/coverage.smk delete mode 100644 workflow/rules/coverage_tracks.smk delete mode 100644 workflow/scripts/get_genome.py diff --git a/.test/config/config.yml b/.test/config/config.yml index bce0678..6a0a7c9 100644 --- a/.test/config/config.yml +++ b/.test/config/config.yml @@ -11,7 +11,7 @@ get_genome: "RefSeq": "gene", "RefSeq": "pseudogene", "RefSeq": "CDS", - "Protein Homology": "CDS" + "Protein Homology": "CDS", ] extract_features: @@ -22,37 +22,22 @@ umi_extraction: pattern: "--bc-pattern=NNNCCCNNNCCCNNNC" umi_dedup: "--edit-distance-threshold=0" -cutadapt: - read1_adapter: "TACACGACGCTCTTCCGATCT" - read2_adapter: "AGATCGGAAGAGCGTCGTGTA" - default: - [ - "-q 10 ", - "-m 18 ", - "--overlap=3" - ] +fastp: + extra: "-M 10 -l 18 --adapter_sequence TACACGACGCTCTTCCGATCT --adapter_sequence_r2 AGATCGGAAGAGCGTCGTGTA" star: - index: Null - genomeSAindexNbases: 9 - multi: 10 - sam_multi: 1 - intron_max: 1 - default: + index: "--genomeSAindexNbases 9" + extra: [ - "--readFilesCommand zcat ", - "--outSAMstrandField None ", - "--outSAMattributes All ", - "--outSAMattrIHstart 0 ", - "--outFilterType Normal ", - "--outFilterMultimapScoreRange 1 ", - "-o STARmappings ", - "--outSAMtype BAM Unsorted ", - "--outStd BAM_Unsorted ", + "--outFilterMultimapNmax 10 ", + "--outSAMmultNmax 1", "--outMultimapperOrder Random ", - "--alignEndsType EndToEnd", ] +samtools: + sort: "" + index: "" + feature_counts: defaults: [ @@ -61,18 +46,16 @@ feature_counts: "-g locus_tag", "-M", "--fracOverlap 0.2", - "--largestOverlap" + "--largestOverlap", ] deeptools: - bin_size: 1 - normalize: "CPM" - defaults: "--exactScaling" - paired_end: "--extendReads" + genome_size: 2000000 + extra: "--binSize 1 --normalizeUsing CPM --exactScaling --extendReads" + +fastqc: + extra: "--quiet --nogroup" multiqc: - config: "config/multiqc_config.yml" - defaults: ["--dirs-depth 2 ", - "--exclude general_stats ", - "--force ", - "--dirs"] + config: "config/multiqc_config.yml" + extra: "--dirs" diff --git a/.test/config/samplesheet/samples_mpusp_custom.tsv b/.test/config/samplesheet/samples_mpusp_custom.tsv index 58750fe..0ac3416 100644 --- a/.test/config/samplesheet/samples_mpusp_custom.tsv +++ b/.test/config/samplesheet/samples_mpusp_custom.tsv @@ -1,3 +1,3 @@ -sample condition replicate experiment fq1 fq2 fq_umi -RNA-1 RNA 1 rnaseq_mpusp_custom data/rnaseq_mpusp_custom/RNA-1_R1.fastq.gz data/rnaseq_mpusp_custom/RNA-1_R2.fastq.gz - -RNA-2 RNA 2 rnaseq_mpusp_custom data/rnaseq_mpusp_custom/RNA-2_R1.fastq.gz data/rnaseq_mpusp_custom/RNA-2_R2.fastq.gz - +sample condition replicate read1 read2 readumi +RNA-1 RNA 1 data/rnaseq_mpusp_custom/RNA-1_R1.fastq.gz data/rnaseq_mpusp_custom/RNA-1_R2.fastq.gz - +RNA-2 RNA 2 data/rnaseq_mpusp_custom/RNA-2_R1.fastq.gz data/rnaseq_mpusp_custom/RNA-2_R2.fastq.gz - diff --git a/.test/config/samplesheet/samples_neb_umi.tsv b/.test/config/samplesheet/samples_neb_umi.tsv index fcf7ad3..a480d41 100644 --- a/.test/config/samplesheet/samples_neb_umi.tsv +++ b/.test/config/samplesheet/samples_neb_umi.tsv @@ -1,3 +1,3 @@ -sample condition replicate experiment fq1 fq2 fq_umi +sample condition replicate experiment fq1 fq2 read1 RNA-1 RNA 1 rnaseq_neb_umi data/rnaseq_neb_umi/RNA-1_R1.fastq.gz data/rnaseq_neb_umi/RNA-1_R2.fastq.gz data/rnaseq_neb_umi/RNA-1_UMI.fastq.gz RNA-2 RNA 2 rnaseq_neb_umi data/rnaseq_neb_umi/RNA-2_R1.fastq.gz data/rnaseq_neb_umi/RNA-2_R2.fastq.gz data/rnaseq_neb_umi/RNA-2_UMI.fastq.gz diff --git a/.test/config/samplesheet/samples_nextflex.tsv b/.test/config/samplesheet/samples_nextflex.tsv index a35df67..ae546d2 100644 --- a/.test/config/samplesheet/samples_nextflex.tsv +++ b/.test/config/samplesheet/samples_nextflex.tsv @@ -1,3 +1,3 @@ -sample condition replicate experiment fq1 fq2 fq_umi +sample condition replicate experiment fq1 fq2 read1 RNA-1 RNA 1 rnaseq_nextflex data/rnaseq_nextflex/RNA-1_R1.fastq.gz RNA-1_R2.fastq.gz - RNA-2 RNA 2 rnaseq_nextflex data/rnaseq_nextflex/RNA-2_R1.fastq.gz RNA-2_R2.fastq.gz - diff --git a/config/README.md b/config/README.md index 60f77f7..2f3a025 100644 --- a/config/README.md +++ b/config/README.md @@ -34,7 +34,7 @@ Important requirements when using custom `*.fasta` and `*.gff` files: RNA sequencing data in `*.fastq.gz` format. The currently supported input data are **second generation reads**. Input data files are supplied via a mandatory table, whose location is indicated in the `config.yml` file (default: `samples.tsv`). The sample sheet has the following layout: -| sample | condition | replicate | experiment | fq1 | fq2 | fq_umi | +| sample | condition | replicate | experiment | fq1 | fq2 | read1 | | ------ | --------- | --------- | ------------------- | ----------------- | ----------------- | ------ | | RNA-1 | RNA | 1 | rnaseq_mpusp_custom | RNA-1_R1.fastq.gz | RNA-1_R2.fastq.gz | \- | | RNA-2 | RNA | 2 | rnaseq_mpusp_custom | RNA-2_R2.fastq.gz | RNA-2_R2.fastq.gz | \- | diff --git a/workflow/Snakefile b/workflow/Snakefile index 05b99de..b30b81b 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -42,7 +42,7 @@ include: "rules/trim.smk" include: "rules/dedup.smk" include: "rules/mapping.smk" include: "rules/quantification.smk" -include: "rules/coverage_tracks.smk" +include: "rules/coverage.smk" onstart: diff --git a/workflow/envs/cutadapt.yml b/workflow/envs/cutadapt.yml deleted file mode 100644 index e6bbff7..0000000 --- a/workflow/envs/cutadapt.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: cutadapt -channels: - - conda-forge - - bioconda -dependencies: - - cutadapt=4.9 diff --git a/workflow/envs/deeptools.yml b/workflow/envs/deeptools.yml deleted file mode 100644 index e9de356..0000000 --- a/workflow/envs/deeptools.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: deeptools -channels: - - conda-forge - - bioconda -dependencies: - - deeptools=3.5.5 diff --git a/workflow/envs/fastqc.yml b/workflow/envs/fastqc.yml deleted file mode 100644 index 25e6d86..0000000 --- a/workflow/envs/fastqc.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: fastqc -channels: - - conda-forge - - bioconda -dependencies: - - fastqc=0.12.1 diff --git a/workflow/envs/get_genome.yml b/workflow/envs/get_genome.yml deleted file mode 100644 index 26ce7cc..0000000 --- a/workflow/envs/get_genome.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: get_genome -channels: - - conda-forge - - bioconda -dependencies: - - unzip=6.0 - - ncbi-datasets-cli=16.27.2 - - bcbio-gff=0.7.1 - - samtools=1.21 - - htslib=1.21 diff --git a/workflow/envs/multiqc.yml b/workflow/envs/multiqc.yml deleted file mode 100644 index 502ba91..0000000 --- a/workflow/envs/multiqc.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: multiqc -channels: - - conda-forge - - bioconda -dependencies: - - multiqc=1.24.1 diff --git a/workflow/envs/samtools.yml b/workflow/envs/samtools.yml deleted file mode 100644 index d6a5cca..0000000 --- a/workflow/envs/samtools.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: samtools -channels: - - conda-forge - - bioconda -dependencies: - - samtools=1.21 - - htslib=1.21 diff --git a/workflow/envs/star.yml b/workflow/envs/star.yml deleted file mode 100644 index f1ae198..0000000 --- a/workflow/envs/star.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: star -channels: - - conda-forge - - bioconda -dependencies: - - star=2.7.11b - - samtools=1.21 - - htslib=1.21 diff --git a/workflow/rules/common.smk b/workflow/rules/common.smk index a765581..d8173b7 100644 --- a/workflow/rules/common.smk +++ b/workflow/rules/common.smk @@ -1,15 +1,17 @@ import os import pandas as pd -import yaml from snakemake.logging import logger +from pathlib import Path # read sample sheet # ----------------------------------------------------- -na_values = ["", "NaN", "nan", "null", "-"] samples = ( pd.read_csv( - config["samplesheet"], sep="\t", dtype={"sample": str}, na_values=na_values + config["samplesheet"], + sep="\t", + dtype={"sample": str}, + na_values=["", "NaN", "nan", "null", "-"], ) .set_index("sample", drop=False) .sort_index() @@ -18,169 +20,50 @@ samples = ( wildcard_constraints: sample="|".join(samples.index), - read="|".join(["R1", "R2"]), + read="|".join(["read1", "read2", "readumi"]), # helpers # ----------------------------------------------------- -experiment_types = list(samples["experiment"].unique()) - -is_single_end_experiment = False -is_paired_end_experiment = False -is_rnaseq_neb_umi = False -is_rnaseq_nextflex = False -is_rnaseq_neb_umi = False -is_rnaseq_mpusp_custom = False - -# set flags based on experiment types -if "rnaseq_neb_umi" in experiment_types: - is_rnaseq_neb_umi = True -if "rnaseq_nextflex" in experiment_types: - is_rnaseq_nextflex = True -if "rnaseq_mpusp_custom" in experiment_types: - is_rnaseq_mpusp_custom = True - -# if any entry in fq2 is NaN -> single-end experiment -if samples["fq2"].isna().any(): - is_single_end_experiment = True - -# if any entry in fq2 is not NaN -> paired-end experiment -if samples["fq2"].notna().any(): - is_paired_end_experiment = True - - -def validate_experiment_type(a, b, c=False): - return sum([a, b, c]) == 1 - - -# each rnaseq experiment type can only be used exclusively -if not validate_experiment_type( - is_rnaseq_neb_umi, is_rnaseq_nextflex, is_rnaseq_mpusp_custom -): - msg = "A single experiment type needs to be selected. Possible types are: [rnaseq_neb_umi, rnaseq_nextflex, rnaseq_mpusp_custom]." - logger.error(msg) - raise ValueError(msg) - -# either single-end or paired-end sequencing allowed -if not validate_experiment_type(a=is_single_end_experiment, b=is_paired_end_experiment): - msg = "All samples need to be sequenced either in single-end or paired-end mode in order to analyse them at once." - logger.error(msg) - raise ValueError(msg) - - -# set final output files -# ----------------------------------------------------- -def get_final_output(): - targets = [] - targets.append("results/report/multiqc_report.html") - targets.append( - expand( - "results/{step}/wig_normalized/{sample}_cpm_{strand}.bw", - step=["mapped", "deduplicated"], - sample=samples.index, - strand=["plus", "minus"], +# determine input type, all entries must be either single or paired end +def is_paired_end(): + if samples["read2"].isna().all(): + return False + elif samples["read2"].notna().all(): + return True + else: + msg = ( + "Some samples seem to have a read2 fastq file, while others have only a " + + "read1 fastq file. \nYou may not mix single-end and paired-end samples." ) - ) - return targets - - -# returns True if single-end -def is_single_end(sample): - """Determine whether the sample is single-end.""" - fq2_not_present = pd.isnull(samples.loc[sample, "fq2"]) - return fq2_not_present - - -# get fastq files for qc input -def get_qc_input(wildcards): - """Get QC input file.""" - inputs = [] - if wildcards.status == "raw": - inputs += [samples.loc[wildcards.sample]["fq1"]] - if not is_single_end(wildcards.sample): - # append R2 if sample is paired-end - inputs += [samples.loc[wildcards.sample]["fq2"]] - if wildcards.status == "clipped": - if not is_single_end(wildcards.sample): - inputs += expand( - "results/umi_extract/{sample}_{read}.fastq.gz", - sample=wildcards.sample, - read=["R1", "R2"], - ) - else: - inputs += expand( - "results/clipped/{sample}.fastq.gz", - sample=wildcards.sample, - ) - return inputs - + logger.error(msg) + raise ValueError(msg) -# get fastq files for umi extract input -def get_umi_input(wildcards): - """Get FASTQ files UMI extraction.""" - inputs = [] - # three fastq files - inputs.append(samples.loc[wildcards.sample]["fq1"]) - if not is_single_end(wildcards.sample): - # append R2 if sample is paired-end - inputs.append(samples.loc[wildcards.sample]["fq2"]) - # include third fastq file - if is_rnaseq_neb_umi: - inputs.append(samples.loc[wildcards.sample]["fq_umi"]) - return inputs +# test presence of read1, read2, and umi_read +def get_reads(): + reads = ["read1", "read2"] if is_paired_end() else ["read1"] + reads += ["readumi"] if samples["readumi"].notna().all() else [] + return reads -# returns fastq files for trimming -def get_trimming_input(wildcards): - """Get FASTQ files for trimming.""" - inputs = [] - if is_single_end_experiment: - inputs += expand("results/umi_extract/{sample}.fastq.gz", sample=wildcards.sample) - elif is_paired_end_experiment: - inputs += expand( - "results/umi_extract/{sample}_{read}.fastq.gz", - sample=wildcards.sample, - read=["R1", "R2"], - ) - return inputs - -# returns fastq files for truncation post trimming -def get_trunc_input(wildcards): - """Get FASTQ files for trunction post trimming.""" - inputs = [] - if is_single_end_experiment: - inputs += expand("results/clipped/{sample}.fastq.gz", sample=wildcards.sample) - elif is_paired_end_experiment: - inputs += expand( - "results/clipped/{sample}_{read}.fastq.gz", - sample=wildcards.sample, - read=["R1", "R2"], - ) - return inputs +# get fastq files +def get_fastq(wildcards): + file = Path(samples.loc[wildcards["sample"]][wildcards["read"]]) + if file.is_absolute(): + return file + else: + input_dir = Path.absolute(Path.cwd()) + return input_dir / file -# returns fastq files for mapping -def get_mapping_input(wildcards): - """Get FASTQ files for trimming.""" - inputs = [] - if is_single_end_experiment and (is_rnaseq_mpusp_custom or is_rnaseq_neb_umi): - inputs += expand("results/clipped/{sample}.fastq.gz", sample=wildcards.sample) - elif is_single_end_experiment and is_rnaseq_nextflex: - inputs += expand("results/trunc_fastq/{sample}.fastq.gz", sample=wildcards.sample) - elif is_paired_end_experiment and (is_rnaseq_mpusp_custom or is_rnaseq_neb_umi): - inputs += expand( - "results/clipped/{sample}_{read}.fastq.gz", - sample=wildcards.sample, - read=["R1", "R2"], - ) - elif is_paired_end_experiment and is_rnaseq_nextflex: - inputs += expand( - "results/trunc_fastq/{sample}_{read}.fastq.gz", - sample=wildcards.sample, - read=["R1", "R2"], - ) - return inputs +# get pairs of fastq files for fastp +def get_fastq_pairs(wildcards): + return expand( + "results/umi_extract/{sample}_{read}.fastq.gz", + sample=wildcards.sample, + read=get_reads(), + ) # return bam files for alignment qc @@ -197,20 +80,6 @@ def get_stats_input(wildcards): ) -# return bam files to generate coverage tracks -def get_bigwig_input(wildcards): - if wildcards.step == "mapped": - return expand( - "results/mapped/{sample}.bam", - sample=wildcards.sample, - ) - if wildcards.step == "deduplicated": - return expand( - "results/deduplicated/{sample}.bam", - sample=wildcards.sample, - ) - - # returns path to conda envs files def get_conda_envs_files(): wf_dir = os.path.abspath(workflow.basedir) @@ -225,33 +94,45 @@ def get_conda_envs_files(): return envs -def construct_multiqc_input(): +def get_multiqc_input(wildcards): inputs = [] inputs += expand( - "results/qc/{status}_reads/{sample}", - status=["raw", "clipped"], + "results/fastqc/{sample}_{read}_fastqc.{ext}", + sample=samples.index, + read=["read1", "read2"] if is_paired_end() else ["read1"], + ext=["html", "zip"], + ) + inputs += expand( + "results/mapped/unsorted/{sample}/mapped.bam", sample=samples.index, ) inputs += expand( - "results/qc/{step}_alignment/{sample}.flagstat", - step=["mapped", "dedup"], + "results/deduplicated/log/{sample}_umi_stats.txt", + sample=samples.index, + ) + inputs += expand( + "results/qc/{step}/{sample}.flagstat", + step=["mapped", "deduplicated"], sample=samples.index, ) inputs += expand( "results/qc/biotypes/{sample}.counts.summary", sample=samples.index, ) - inputs.append(["results/qc/biotypes/barplot_biotype_data_mqc.json"]) + inputs += ["results/qc/biotypes/barplot_biotype_data_mqc.json"] return inputs -def define_multiqc_dirs(): - dirs = [] - prefix = "results" - dirs.append(os.path.join(prefix, "qc/raw_reads")) - dirs.append(os.path.join(prefix, "clipped")) - dirs.append(os.path.join(prefix, "qc/clipped_reads")) - dirs.append(os.path.join(prefix, "mapped/unsorted")) - dirs.append(os.path.join(prefix, "deduplicated")) - dirs.append(os.path.join(prefix, "qc/biotypes")) - return " ".join(dirs) +# get final output files +def get_final_output(): + targets = [] + targets.append("results/multiqc/multiqc_report.html") + targets.append( + expand( + "results/{step}/{sample}_cpm_{strand}.bw", + step=["mapped", "deduplicated"], + sample=samples.index, + strand=["plus", "minus"], + ) + ) + return targets diff --git a/workflow/rules/coverage.smk b/workflow/rules/coverage.smk new file mode 100644 index 0000000..9b9c3e7 --- /dev/null +++ b/workflow/rules/coverage.smk @@ -0,0 +1,25 @@ +# -------------------------------------------------------------- +# module to generate normalized coverage tracks using deeptools +# Note: deeptools implements the strand selection in the counter intuitive way: +# This means, that when using deeptools for extracting coverage files, +# strand must always be switched. forward == reverse! +# -------------------------------------------------------------- +rule deeptools_coverage: + input: + bam="results/{step}/{sample}.bam", + bai="results/{step}/{sample}.bam.bai", + output: + bw="results/{step}/{sample}_cpm_{strand}.bw", + threads: int(workflow.cores * 0.2) + params: + effective_genome_size=config["deeptools"]["genome_size"], + extra=lambda wc: ( + config["deeptools"]["extra"] + + f" --filterRNAstrand {('reverse' if(config['libtype']== 'sense' and wc.strand== 'plus') or(config['libtype']== 'antisense' and wc.strand== 'minus') else 'forward')}" + ), + log: + "results/{step}/{sample}_cpm_{strand}.log", + message: + "generate normalized coverage files using deeptools" + wrapper: + "v7.0.0/bio/deeptools/bamcoverage" diff --git a/workflow/rules/coverage_tracks.smk b/workflow/rules/coverage_tracks.smk deleted file mode 100644 index a88b9a7..0000000 --- a/workflow/rules/coverage_tracks.smk +++ /dev/null @@ -1,48 +0,0 @@ -# -------------------------------------------------------------- -# module to generate normalized coverage tracks using deeptools -# Note: deeptools implements the strand selection in the counter intuitive way: -# This means, that when using deeptools for extracting coverage files, -# strand must always be switched. forward == reverse! -# -------------------------------------------------------------- -rule normalize_bw: - input: - get_bigwig_input, - output: - "results/{step}/wig_normalized/{sample}_cpm_{strand}.bw", - conda: - "../envs/deeptools.yml" - message: - """--- Generate normalized bigwig coverage file.""" - log: - log="results/{step}/wig_normalized/log/{sample}_cpm_{strand}.log", - stderr="results/{step}/wig_normalized/log/{sample}_cpm_{strand}.stderr", - params: - bin=lambda wc: config["deeptools"]["bin_size"], - norm=lambda wc: config["deeptools"]["normalize"], - defaults=lambda wc: ( - " ".join( - [config["deeptools"]["defaults"], config["deeptools"]["paired_end"]] - ) - if is_paired_end_experiment - else config["deeptools"]["defaults"] - ), - libtype=lambda wc: config["libtype"], - strand=lambda wc: wc.strand, - threads: int(workflow.cores * 0.2) # assign 20% of max cores - shell: - "if [ {params.libtype} == 'sense' ] && [ {params.strand} == 'plus' ]; then " - "strand=`echo -e 'reverse'`; " - "elif [ {params.libtype} == 'antisense' ] && [ {params.strand} == 'plus' ]; then " - "strand=`echo -e 'forward'`; " - "elif [ {params.libtype} == 'sense' ] && [ {params.strand} == 'minus' ]; then " - "strand=`echo -e 'forward'`; " - "else strand=`echo -e 'reverse'`; " - "fi; " - "bamCoverage --bam {input} " - "--binSize {params.bin} " - "--normalizeUsing {params.norm} " - "--outFileFormat bigwig " - "-p {threads} " - "{params.defaults} " - "--filterRNAstrand ${{strand}} " - "-o {output} > {log.log} 2> {log.stderr}" diff --git a/workflow/rules/dedup.smk b/workflow/rules/dedup.smk index b7c9a51..8d9f781 100644 --- a/workflow/rules/dedup.smk +++ b/workflow/rules/dedup.smk @@ -1,160 +1,93 @@ -# --------------------------------------------------------------- -# module to extract umis - umi is located in separate fastq file -# --------------------------------------------------------------- -if is_single_end_experiment and is_rnaseq_neb_umi: - - rule umi_extract: - input: - get_umi_input, - output: - fastq="results/umi_extract/{sample}.fastq.gz", - conda: - "../envs/umitools.yml" - log: - path="results/umi_extract/log/{sample}.log", - message: - """--- Extracting UMIs.""" - script: - "../scripts/extract_umis.py" - - -if is_paired_end_experiment and is_rnaseq_neb_umi: - - rule umi_extract_pe: - input: - get_umi_input, - output: - R1="results/umi_extract/{sample}_R1.fastq.gz", - R2="results/umi_extract/{sample}_R2.fastq.gz", - conda: - "../envs/umitools.yml" - log: - path="results/umi_extract/log/{sample}.log", - message: - """--- Extracting UMIs.""" - script: - "../scripts/extract_umis.py" - - -# --------------------------------------------------------------- -# module to extract umis - umi is located in read sequence -# --------------------------------------------------------------- -if is_single_end_experiment and (is_rnaseq_mpusp_custom or is_rnaseq_nextflex): - - rule umi_extract: - input: - fastq=get_umi_input, - output: - fastq="results/umi_extract/{sample}.fastq.gz", - conda: - "../envs/umitools.yml" - message: - """--- Extracting UMIs.""" - params: - method=config["umi_extraction"]["method"], - pattern=config["umi_extraction"]["pattern"], - log: - path="results/umi_extract/log/{sample}.log", - error="results/umi_extract/log/{sample}.err", - shell: - "umi_tools extract " - "--extract-method={params.method} " - "{params.pattern} " - "--stdin {input.fastq} " - "--stdout {output.fastq} " - "--log={log.path} 2> {log.error}" - - -if is_paired_end_experiment and (is_rnaseq_mpusp_custom or is_rnaseq_nextflex): - - rule umi_extract_pe: - input: - fastqs=get_umi_input, - output: - R1="results/umi_extract/{sample}_R1.fastq.gz", - R2="results/umi_extract/{sample}_R2.fastq.gz", - conda: - "../envs/umitools.yml" - message: - """--- Extracting UMIs.""" - params: - method=config["umi_extraction"]["method"], - pattern=config["umi_extraction"]["pattern"], - log: - path="results/umi_extract/log/{sample}.log", - error="results/umi_extract/log/{sample}.err", - shell: - "umi_tools extract " - "--extract-method={params.method} " - "{params.pattern} " - "--stdin={input.fastqs[0]} " - "--read2-in={input.fastqs[1]} " - "--stdout={output.R1} " - "--read2-out={output.R2} " - "--log={log.path} 2> {log.error}" - - -# --------------------------------------------- -# module to deduplicate reads via UMIs -# --------------------------------------------- -if is_single_end_experiment: - - rule umi_dedup: - input: - bam="results/mapped/{sample}.bam", - bai="results/mapped/{sample}.bam.bai", - output: - bam="results/deduplicated/{sample}.bam", - bai="results/deduplicated/{sample}.bam.bai", - conda: - "../envs/umitools.yml" - message: - """--- UMI tools deduplication.""" - params: - tmp="results/deduplicated/sort_{sample}_tmp", - default=config["umi_dedup"], - log: - path="results/deduplicated/log/{sample}.log", - stderr="results/deduplicated/log/{sample}.stderr", - stats="results/deduplicated/log/{sample}_umi_stats.txt", - threads: int(workflow.cores * 0.2) # assign 25% of max cores. - shell: - "umi_tools dedup " - "{params.default} " - "--stdin={input.bam} " - "--output-stats={log.stats} " - "--log={log.path} 2> {log.stderr} | " - "samtools sort -@ {threads} -O bam -T {params.tmp} -o {output.bam}; " - "samtools index {output.bam}" - - -if is_paired_end_experiment: - - rule umi_dedup_pe: - input: - bam="results/mapped/{sample}.bam", - bai="results/mapped/{sample}.bam.bai", - output: - bam="results/deduplicated/{sample}.bam", - bai="results/deduplicated/{sample}.bam.bai", - conda: - "../envs/umitools.yml" - message: - """--- UMI tools deduplication.""" - params: - tmp="results/deduplicated/sort_{sample}_tmp", - default=config["umi_dedup"], - log: - path="results/deduplicated/log/{sample}.log", - stderr="results/deduplicated/log/{sample}.stderr", - stats="results/deduplicated/log/{sample}_umi_stats.txt", - threads: int(workflow.cores * 0.2) # assign 25% of max cores. - shell: - "umi_tools dedup " - "--paired " - "{params.default} " - "--stdin={input.bam} " - "--output-stats={log.stats} " - "--log={log.path} 2> {log.stderr} | " - "samtools sort -@ {threads} -O bam -T {params.tmp} -o {output.bam}; " - "samtools index {output.bam}" +rule get_fastq: + input: + get_fastq, + output: + fastq="results/get_fastq/{sample}_{read}.fastq.gz", + conda: + "../envs/base.yml" + message: + "obtaining fastq files" + log: + "results/get_fastq/{sample}_{read}.log", + shell: + "ln -s {input} {output.fastq};" + "echo 'made symbolic link from {input} to {output.fastq}' > {log}" + + +rule umi_extract_standard: + input: + fq1="results/get_fastq/{sample}_read1.fastq.gz", + fq2="results/get_fastq/{sample}_read2.fastq.gz" if is_paired_end() else "", + output: + fq1="results/umi_extract/{sample}_read1.fastq.gz", + fq2="results/umi_extract/{sample}_read2.fastq.gz" if is_paired_end() else "", + conda: + "../envs/umitools.yml" + message: + "--- Extracting UMIs." + params: + method=config["umi_extraction"]["method"], + pattern=config["umi_extraction"]["pattern"], + read2=( + lambda wildcards, input, output: ( + f"--read2-in {input.fq2} --read2-out {output.fq2}" if input.fq2 else "" + ) + ), + log: + path="results/umi_extract/log/{sample}.log", + error="results/umi_extract/log/{sample}.err", + shell: + """ + umi_tools extract \ + --extract-method {params.method} \ + {params.pattern} \ + --stdin {input.fq1} \ + --stdout {output.fq1} \ + {params.read2} \ + --log {log.path} 2> {log.error} + """ + + +# rule umi_extract_separate: +# input: +# "results/get_fastq/{sample}_{read}.fastq.gz", +# output: +# "results/umi_extract/{sample}_{read}.fastq.gz", +# conda: +# "../envs/umitools.yml" +# log: +# "results/umi_extract/log/{sample}_{read}.log", +# message: +# """--- Extracting UMIs.""" +# script: +# "../scripts/extract_umis.py" + + +rule umi_dedup_pe: + input: + bam="results/mapped/{sample}.bam", + bai="results/mapped/{sample}.bam.bai", + output: + bam="results/deduplicated/{sample}.bam", + bai="results/deduplicated/{sample}.bam.bai", + conda: + "../envs/umitools.yml" + message: + """--- UMI tools deduplication.""" + params: + tmp="results/deduplicated/sort_{sample}_tmp", + default=config["umi_dedup"], + log: + path="results/deduplicated/log/{sample}.log", + stderr="results/deduplicated/log/{sample}.stderr", + stats="results/deduplicated/log/{sample}_umi_stats.txt", + threads: int(workflow.cores * 0.2) # assign 25% of max cores. + shell: + "umi_tools dedup " + "--paired " + "{params.default} " + "--stdin={input.bam} " + "--output-stats={log.stats} " + "--log={log.path} 2> {log.stderr} | " + "samtools sort -@ {threads} -O bam -T {params.tmp} -o {output.bam}; " + "samtools index {output.bam}" diff --git a/workflow/rules/mapping.smk b/workflow/rules/mapping.smk index 41ce6fc..9479716 100644 --- a/workflow/rules/mapping.smk +++ b/workflow/rules/mapping.smk @@ -1,113 +1,93 @@ -# ----------------------------------------------------- -# module to fetch genome from NCBI or Ensemble -# ----------------------------------------------------- rule get_genome: + input: + fasta=lambda wildcards: ( + config["get_genome"]["fasta"] + if config["get_genome"]["database"] == "manual" + else [] + ), + gff=lambda wildcards: ( + config["get_genome"]["gff"] + if config["get_genome"]["database"] == "manual" + else [] + ), output: - path=directory("results/genome"), fasta="results/genome/genome.fasta", gff="results/genome/genome.gff", - conda: - "../envs/get_genome.yml" + fai="results/genome/genome.fasta.fai", message: """--- Parsing genome GFF and FASTA files.""" params: database=config["get_genome"]["database"], assembly=config["get_genome"]["assembly"], - fasta=config["get_genome"]["fasta"], - gff=config["get_genome"]["gff"], + gff_source_types=config["get_genome"]["gff_source_type"], log: - path="results/genome/log/get_genome.log", - script: - "../scripts/get_genome.py" + "results/genome/get_genome.log", + wrapper: + "https://raw.githubusercontent.com/MPUSP/mpusp-snakemake-wrappers/refs/heads/main/get_genome" -# ----------------------------------------------------- -# module to map reads to ref genome using STAR aligner -# ----------------------------------------------------- -rule create_star_index: +rule star_index: input: - genome="results/genome/genome.fasta", + fasta=rules.get_genome.output.fasta, output: - path=directory("results/genome/index"), - conda: - "../envs/star.yml" - message: - """--- STAR index creation.""" + directory("results/mapped/index/"), + threads: 1 params: - index=config["star"]["index"], - indexNbases=config["star"]["genomeSAindexNbases"], + extra=config["star"]["index"], log: - path="results/genome/log/star_index.log", - shell: - "if [ {params.index} == None ]; then " - "mkdir {output.path};" - "STAR --runMode genomeGenerate " - "--genomeDir {output.path} " - "--genomeFastaFiles {input.genome} " - "--genomeSAindexNbases {params.indexNbases} > {log.path}; " - "rm -f ./Log.out; " - "else " - "ln -s {params.index} {output.path}; " - "echo 'made symbolic link from {params.index} to {output.path}' > {log.path}; " - "fi;" + "results/mapped/index/index.log", + message: + "--- Create STAR index." + wrapper: + "v7.2.0/bio/star/index" -# ----------------------------------------------------- -# module to map reads to ref genome using STAR aligner -# ----------------------------------------------------- rule star_mapping: input: - fastqs=get_mapping_input, - genome=rules.create_star_index.output, + fq1="results/fastp/{sample}_read1.fastq.gz", + fq2="results/fastp/{sample}_read2.fastq.gz" if is_paired_end() else "", + idx=rules.star_index.output, output: - bam="results/mapped/unsorted/{sample}.bam", - conda: - "../envs/star.yml" + aln="results/mapped/unsorted/{sample}/mapped.bam", + log_final="results/mapped/unsorted/{sample}/Log.final.out", + log: + "results/mapped/unsorted/{sample}/star.log", message: - """--- STAR mapping.""" + "--- STAR mapping." params: - default=config["star"]["default"], - multi=config["star"]["multi"], - sam_multi=config["star"]["sam_multi"], - intron_max=config["star"]["intron_max"], - outprefix=lambda w, output: f"{os.path.splitext(output.bam)[0]}_", - input_str=lambda w, input: ( - " ".join(input.fastqs) if len(input.fastqs) == 2 else input.fastqs - ), + extra=config["star"]["extra"], + threads: int(workflow.cores * 0.2) + wrapper: + "v7.2.0/bio/star/align" + + +rule samtools_sort: + input: + "results/mapped/unsorted/{sample}/mapped.bam", + output: + "results/mapped/{sample}.bam", log: - "results/mapped/log/{sample}.log", - threads: int(workflow.cores * 0.2) if int(workflow.cores * 0.2) >= 1 else 1 # assign 20% of max cores. - shell: - "STAR " - "--runThreadN {threads} " - "--genomeDir {input.genome} " - "--readFilesIn {params.input_str} " - "{params.default} " - "--outFilterMultimapNmax {params.multi} " - "--alignIntronMax {params.intron_max} " - "--outSAMmultNmax {params.sam_multi} " - "--outFileNamePrefix {params.outprefix} " - "> {output.bam} 2> {log}" + "results/mapped/{sample}.log", + message: + "--- Sort reads after mapping." + params: + extra=config["samtools"]["sort"], + threads: 2 + wrapper: + "v7.0.0/bio/samtools/sort" -# --------------------------------------------------- -# module to sort and index bam file using samtools -# --------------------------------------------------- -rule mapping_sorted_bam: +rule samtools_index: input: - bam=rules.star_mapping.output.bam, + "results/mapped/{sample}.bam", output: - bam="results/mapped/{sample}.bam", - bai="results/mapped/{sample}.bam.bai", - conda: - "../envs/samtools.yml" + "results/mapped/{sample}.bam.bai", log: - "results/mapped/log/samtools_{sample}.log", + "results/mapped/{sample}_index.log", message: - """--- Samtools sort and index bam files.""" + "--- Index reads." params: - tmp="results/mapped/sort_{sample}_tmp", - threads: int(workflow.cores * 0.2) # assign 20% of max cores - shell: - "samtools sort -@ {threads} -O bam -T {params.tmp} -o {output.bam} {input.bam} &> {log}; " - "samtools index -@ {threads} {output.bam} 2>> {log}" + extra=config["samtools"]["index"], + threads: 2 + wrapper: + "v7.0.0/bio/samtools/index" diff --git a/workflow/rules/qc.smk b/workflow/rules/qc.smk index c648ed8..ae3fe28 100644 --- a/workflow/rules/qc.smk +++ b/workflow/rules/qc.smk @@ -1,85 +1,38 @@ -# ----------------------------------------------------- -# modules to make fastqc report -# ----------------------------------------------------- -if is_single_end_experiment: - - rule fastqc: - input: - fastq=get_qc_input, - output: - report=directory("results/qc/{status}_reads/{sample}"), - conda: - "../envs/fastqc.yml" - message: - """--- Checking fastq files with FastQC.""" - log: - "results/qc/{status}_reads/log/{sample}.log", - threads: int(workflow.cores * 0.2) # assign 20% of max cores - shell: - "mkdir -p {output.report}; " - "fastqc --nogroup --threads {threads} -o {output.report} -q {input.fastq} > {log}" - - -if is_paired_end_experiment: - - rule fastqc: - input: - fastqs=get_qc_input, - output: - report=directory("results/qc/{status}_reads/{sample}"), - conda: - "../envs/fastqc.yml" - message: - """--- Checking fastq files with FastQC.""" - log: - "results/qc/{status}_reads/log/{sample}.log", - threads: int(workflow.cores * 0.2) # assign 20% of max cores - shell: - "mkdir -p {output.report}; " - "fastqc --nogroup --threads {threads} -o {output.report} -q {input.fastqs[0]} > {log}; " - "fastqc --nogroup --threads {threads} -o {output.report} -q {input.fastqs[1]} &> {log}" - - -# ----------------------------------------------------- -# module to determine alignment stats -# ----------------------------------------------------- -rule alignment_stats: +rule fastqc: input: - stats=get_stats_input, + fastq="results/get_fastq/{sample}_{read}.fastq.gz", output: - "results/qc/{step}_alignment/{sample}.flagstat", - conda: - "../envs/samtools.yml" - log: - "results/qc/{step}_alignment/log/samtools_{sample}.log", + html="results/fastqc/{sample}_{read}_fastqc.html", + zip="results/fastqc/{sample}_{read}_fastqc.zip", + params: + extra=config["fastqc"]["extra"], message: - """--- Generate mapping statistics of BAM file using samtools.""" - threads: int(workflow.cores * 0.2) # assign 20% of max cores - shell: - "samtools flagstat -@ {threads} {input.stats} > {output} 2> {log}" + "--- Checking fastq files with FastQC" + log: + "results/fastqc/{sample}.bwa.{read}.log", + threads: 1 + resources: + mem_mb=4096, + wrapper: + "v6.0.0/bio/fastqc" -# ----------------------------------------------------- -# module to qc quantified biotypes by featureCounts -# ----------------------------------------------------- -rule qc_biotype_summary: +rule alignment_stats: input: - "results/quantify_biotypes/{sample}.counts.summary", + "results/{step}/{sample}.bam", output: - "results/qc/biotypes/{sample}.counts.summary", + "results/qc/{step}/{sample}.flagstat", conda: - "../envs/feature_counts.yml" + "../envs/samtools.yml" log: - "results/qc/biotypes/log/copy_{sample}.log", + "results/qc/{step}/{sample}_flagstat.log", message: - """--- Copy biotype quantification summary.""" + "--- Generate mapping statistics of BAM file using samtools." + threads: int(workflow.cores * 0.2) shell: - "cp {input} {output} &> {log}" + "samtools flagstat -@ {threads} {input} > {output} 2> {log}" -# ----------------------------------------------------- -# module to plot biotype distribution -# ----------------------------------------------------- rule qc_biotype_barplot: input: table="results/quantify_biotypes/all_samples_biotype_counts.tsv", @@ -96,9 +49,6 @@ rule qc_biotype_barplot: "../scripts/plot_biotypes.py" -# ----------------------------------------------------- -# module to extract software versions from conda envs -# ----------------------------------------------------- rule get_conda_envs: output: "results/versions/log_conda_envs.txt", @@ -119,19 +69,16 @@ rule get_conda_envs: "fi;" -# ----------------------------------------------------- -# module to generate software version yaml file MultiQC -# ----------------------------------------------------- rule get_software_yaml: input: conda_envs="results/versions/log_conda_envs.txt", output: yaml="results/versions/rnaseq_preprocessinq_mqc_versions.yml", - multi_conf="results/qc/multiqc/multiqc_config.yml", + multi_conf="results/multiqc/multiqc_config.yml", conda: "../envs/base.yml" message: - """--- Generate software version yaml file for MultiQC.""" + "--- Generate software version yaml file for MultiQC." log: path="results/versions/log/yaml_versions.log", params: @@ -140,31 +87,17 @@ rule get_software_yaml: "../scripts/get_versions.py" -# ----------------------------------------------------- -# module to run multiQC on input + processed files -# ----------------------------------------------------- rule multiqc: input: - construct_multiqc_input(), - config="results/qc/multiqc/multiqc_config.yml", + get_multiqc_input, + config="results/multiqc/multiqc_config.yml", output: - report="results/qc/multiqc/multiqc_report.html", - final="results/report/multiqc_report.html", - conda: - "../envs/multiqc.yml" + report="results/multiqc/multiqc_report.html", + params: + extra=config["multiqc"]["extra"], message: - """--- Generating MultiQC report for seq data.""" + "--- Generating MultiQC report for seq data" log: - path="results/qc/multiqc/log/multiqc.log", - params: - defaults=config["multiqc"]["defaults"], - outdir=lambda w, output: os.path.split(output.report)[0], - filename=lambda w, output: os.path.split(output.report)[1], - qc_dirs=define_multiqc_dirs(), - shell: - "multiqc {params.defaults} " - "--config {input.config} " - "--outdir {params.outdir} " - "--filename {params.filename} " - "--dirs {params.qc_dirs} &> {log.path}; " - "cp {output.report} {output.final}" + "results/multiqc/multiqc.log", + wrapper: + "v7.5.0/bio/multiqc" diff --git a/workflow/rules/quantification.smk b/workflow/rules/quantification.smk index bfb83ed..ee224c9 100644 --- a/workflow/rules/quantification.smk +++ b/workflow/rules/quantification.smk @@ -1,6 +1,3 @@ -# ----------------------------------------------------- -# module to extract selected biotypes from gff file -# ----------------------------------------------------- rule extract_features: input: gff="results/genome/genome.gff", @@ -9,7 +6,7 @@ rule extract_features: conda: "../envs/extract_features.yml" message: - """--- Extract selected biotype features from genome annotation.""" + "--- Extract selected biotype features from genome annotation." params: features=config["extract_features"]["biotypes"], log: @@ -18,9 +15,6 @@ rule extract_features: "../scripts/extract_features.py" -# ----------------------------------------------------- -# module to generate gtf file from gff -# ----------------------------------------------------- rule gff2gtf: input: gff="results/extracted_features/biotypes.gff", @@ -29,89 +23,49 @@ rule gff2gtf: conda: "../envs/extract_features.yml" message: - """--- gff to gtf conversion.""" + "--- gff to gtf conversion." log: path="results/extracted_features/log/gff2gtf.log", script: "../scripts/gff2gtf.py" -# ----------------------------------------------------- -# module to generate gtf file from gff -# ----------------------------------------------------- -if is_single_end_experiment: - - rule quantify_biotypes: - input: - bam="results/deduplicated/{sample}.bam", - gtf="results/extracted_features/biotypes.gtf", - output: - counts="results/quantify_biotypes/{sample}.counts", - summary="results/quantify_biotypes/{sample}.counts.summary", - conda: - "../envs/feature_counts.yml" - message: - """--- Quantify biotpyes with subread's featureCount.""" - log: - path="results/quantify_biotypes/log/feature_counts_{sample}.log", - threads: min(max(1, int(workflow.cores * 0.2)), 64) # assign 20% of max cores - params: - defaults=config["feature_counts"]["defaults"], - libtype=config["libtype"], - shell: - "if [ {params.libtype} == 'sense' ]; then " - "libtype=`echo -e '-s 1'`; " - "else libtype=`echo -e '-s 2'`; " - "fi; " - "featureCounts -T {threads} " - "{params.defaults} " - "${{libtype}} " - "-a {input.gtf} " - "-o {output.counts} " - "{input.bam} &> {log.path}" - - -if is_paired_end_experiment: - - rule quantify_biotypes: - input: - bam="results/deduplicated/{sample}.bam", - gtf="results/extracted_features/biotypes.gtf", - output: - counts="results/quantify_biotypes/{sample}.counts", - summary="results/quantify_biotypes/{sample}.counts.summary", - conda: - "../envs/feature_counts.yml" - message: - """--- Quantify biotpyes with subread's featureCount.""" - log: - path="results/quantify_biotypes/log/feature_counts_{sample}.log", - threads: min(max(1, int(workflow.cores * 0.2)), 64) # assign 20% of max cores - params: - defaults=config["feature_counts"]["defaults"], - libtype=config["libtype"], - shell: - "if [ {params.libtype} == 'sense' ]; then " - "libtype=`echo -e '-s 1'`; " - "else libtype=`echo -e '-s 2'`; " - "fi; " - "featureCounts -T {threads} " - "{params.defaults} " - "${{libtype}} " - "-a {input.gtf} " - "-p --countReadPairs " - "-o {output.counts} " - "{input.bam} &> {log.path}" +rule quantify_biotypes: + input: + bam="results/deduplicated/{sample}.bam", + gtf="results/extracted_features/biotypes.gtf", + output: + counts="results/qc/biotypes/{sample}.counts", + summary="results/qc/biotypes/{sample}.counts.summary", + conda: + "../envs/feature_counts.yml" + message: + """--- Quantify biotpyes with subread's featureCount.""" + log: + path="results/qc/biotypes/{sample}.counts.log", + threads: min(max(1, int(workflow.cores * 0.2)), 64) # assign 20% of max cores + params: + defaults=config["feature_counts"]["defaults"], + libtype=config["libtype"], + shell: + "if [ {params.libtype} == 'sense' ]; then " + "libtype=`echo -e '-s 1'`; " + "else libtype=`echo -e '-s 2'`; " + "fi; " + "featureCounts -T {threads} " + "{params.defaults} " + "${{libtype}} " + "-a {input.gtf} " + "-p --countReadPairs " + "-o {output.counts} " + "{input.bam} &> {log.path}" -# ----------------------------------------------------------- -# module to combine count tables and add feature information -# ----------------------------------------------------------- rule combine_count_tables: input: - counts=expand("results/quantify_biotypes/{sample}.counts", sample=samples.index), + counts=expand("results/qc/biotypes/{sample}.counts", sample=samples.index), summary=expand( - "results/quantify_biotypes/{sample}.counts.summary", sample=samples.index + "results/qc/biotypes/{sample}.counts.summary", sample=samples.index ), gtf="results/extracted_features/biotypes.gtf", output: @@ -128,9 +82,6 @@ rule combine_count_tables: "../scripts/merge_counts.py" -# ----------------------------------------------------------- -# module to summarize biotype distributions -# ----------------------------------------------------------- rule summarize_biotypes: input: table="results/quantify_biotypes/all_samples_counts.tsv", diff --git a/workflow/rules/trim.smk b/workflow/rules/trim.smk index 71bf164..ae72944 100644 --- a/workflow/rules/trim.smk +++ b/workflow/rules/trim.smk @@ -1,116 +1,21 @@ -if is_single_end_experiment: - - # ----------------------------------------------------- - # module to trim adapters from reads - # ----------------------------------------------------- - rule cutadapt: - input: - fastq=get_trimming_input, - output: - "results/clipped/{sample}.fastq.gz", - conda: - "../envs/cutadapt.yml" - message: - """--- Trim adapters from reads.""" - params: - adapter_R1=config["cutadapt"]["read1_adapter"], - default=config["cutadapt"]["default"], - log: - path="results/clipped/log/{sample}.log", - threads: int(workflow.cores * 0.4) # assign 40% of max cores - shell: - "cutadapt --cores {threads} " - "-a {params.adapter_R1} " - "{params.default} " - "-o {output} " - "{input.fastq} &> {log.path}" - - -if is_single_end_experiment and is_rnaseq_nextflex: - - # ----------------------------------------------------- - # module to remove 4nt from 3' end of reads - # ----------------------------------------------------- - rule truncate_fastq: - input: - fastq=get_trunc_input, - output: - "results/trunc_fastq/{sample}.fastq.gz", - conda: - "../envs/cutadapt.yml" - message: - """--- Trim 4nt from 3' end of reads.""" - log: - path="results/trunc_fastq/log/{sample}.log", - params: - default=config["trunc_fastq"]["default"], - threads: int(workflow.cores * 0.4) # assign 40% of max cores - shell: - "cutadapt --cores {threads} " - "{params.default} " - "-o {output} " - "{input.fastq} &> {log.path}" - - -if is_paired_end_experiment: - - # ----------------------------------------------------- - # module to trim adapters from paired-end reads - # ----------------------------------------------------- - rule cutadapt_pe: - input: - fastqs=get_trimming_input, - output: - R1="results/clipped/{sample}_R1.fastq.gz", - R2="results/clipped/{sample}_R2.fastq.gz", - conda: - "../envs/cutadapt.yml" - message: - """--- Trim adapters from reads.""" - log: - path="results/clipped/log/{sample}.log", - params: - adapter_R1=config["cutadapt"]["read1_adapter"], - adapter_R2=config["cutadapt"]["read2_adapter"], - default=config["cutadapt"]["default"], - input_str=lambda w, input: ( - " ".join(input.fastqs) if len(input.fastqs) == 2 else input.fastqs - ), - threads: int(workflow.cores * 0.4) # assign 40% of max cores - shell: - "cutadapt --cores {threads} " - "-a {params.adapter_R1} " - "-A {params.adapter_R2} " - "{params.default} " - "-o {output.R1} -p {output.R2} " - "{params.input_str} &> {log.path}" - - -if is_paired_end_experiment and is_rnaseq_nextflex: - - # ----------------------------------------------------- - # module to remove 4nt from 3' end of paired-end reads - # ----------------------------------------------------- - rule truncate_fastq_pe: - input: - fastqs=get_trunc_input, - output: - R1="results/trunc_fastq/{sample}_R1.fastq.gz", - R2="results/trunc_fastq/{sample}_R2.fastq.gz", - conda: - "../envs/cutadapt.yml" - message: - """--- Trim 4nt from 3' end of reads.""" - log: - path="results/trunc_fastq/log/{sample}.log", - params: - default=config["trunc_fastq"]["default"], - input_str=lambda w, input: ( - " ".join(input.fastqs) if len(input.fastqs) == 2 else input.fastqs - ), - threads: int(workflow.cores * 0.4) # assign 40% of max cores - shell: - "cutadapt --cores {threads} " - "{params.default} " - "-o {output.R1} -p {output.R2} " - "{params.input_str} &> {log.path}" +rule fastp: + input: + sample=get_fastq_pairs, + output: + html="results/fastp/{sample}.html", + json="results/fastp/{sample}.json", + trimmed=expand( + "results/fastp/{{sample}}_{read}.fastq.gz", + read=get_reads(), + ), + log: + "results/fastp/{sample}.log", + message: + "trimming and QC filtering reads using fastp" + params: + extra=config["fastp"]["extra"], + threads: 2 + resources: + mem_mb=4096, + wrapper: + "v7.0.0/bio/fastp" diff --git a/workflow/scripts/get_genome.py b/workflow/scripts/get_genome.py deleted file mode 100644 index cc96979..0000000 --- a/workflow/scripts/get_genome.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/python - -# GET GENOME -# ----------------------------------------------------------------------------- -# -# This script attempts to download genome sequence (FASTA) and -# genome annotation (GFF / GTF) files from NCBI using the NCBI datasets -# API, or a similar database. Also, the genome sequence is indexed using samtools. -# Alternatively, a FASTA and GFF file can be -# supplied by the user. Input is roughly checked for validity. - -from os import path -from BCBio.GFF import GFFExaminer -from BCBio import GFF -from subprocess import getoutput - -input_database = snakemake.params["database"] -input_assembly = snakemake.params["assembly"] -input_fasta = snakemake.params["fasta"] -input_gff = snakemake.params["gff"] -output_path = snakemake.output["path"] -output_fasta = snakemake.output["fasta"] -output_gff = snakemake.output["gff"] -output_log = snakemake.log["path"] -log = [] -error = [] - - -def check_fasta(input_fasta, log=[], error=[]): - with open(input_fasta, "r") as fasta_file: - fasta = fasta_file.read() - n_items = fasta.count(">") - if n_items: - log += [f"Supplied fasta file '{input_fasta}' was found"] - log += [f"Supplied fasta file contains {n_items} items"] - else: - error += ["The supplied fasta file contains no valid entries starting with '>'"] - return fasta, log, error - - -def check_gff(input_gff, log=[], error=[]): - with open(input_gff, "r") as gff_file: - gff_examiner = GFFExaminer() - log += [f"Supplied GFF file '{input_gff}' was found"] - gff_summary = gff_examiner.available_limits(gff_file) - log += [ - f"Supplied GFF file contains the following items:", - "--------------------", - ] - for item in gff_summary["gff_source_type"]: - log += ["-".join(item) + " : " + str(gff_summary["gff_source_type"][item])] - with open(input_gff, "r") as gff_file: - new_gff = [] - gff_source_type = [] - for i in snakemake.config["get_genome"]["gff_source_type"]: - gff_source_type += list(i.items()) - limits = dict(gff_source_type=gff_source_type) - for rec in GFF.parse(gff_file, limit_info=limits): - for recfeat in rec.features: - rec_keys = recfeat.qualifiers.keys() - if not "Name" in rec_keys: - if "locus_tag" in rec_keys: - recfeat.qualifiers["Name"] = recfeat.qualifiers["locus_tag"] - else: - error += [ - "required fields 'Name','locus_tag' missing in *.gff file" - ] - else: - if "locus_tag" in rec_keys: - recfeat.qualifiers["trivial_name"] = recfeat.qualifiers["Name"] - recfeat.qualifiers["Name"] = recfeat.qualifiers["locus_tag"] - if not "ID" in rec_keys: - if "locus_tag" in rec_keys: - recfeat.qualifiers["ID"] = recfeat.qualifiers["locus_tag"] - elif "Name" in rec_keys: - recfeat.qualifiers["ID"] = recfeat.qualifiers["Name"] - else: - error += [ - "required fields 'ID','locus_tag' missing in *.gff file" - ] - new_gff += [rec] - return new_gff, log, error - - -if input_database.lower() == "ncbi": - ncbi_result = getoutput( - f"datasets summary genome accession {input_assembly} --as-json-lines | " - + "dataformat tsv genome --fields accession,annotinfo-release-date,organism-name" - ) - if ncbi_result.startswith("Error"): - error += [ncbi_result] - error += [ - "The supplied refseq/genbank ID was not valid. Example for correct input: 'GCF_000009045.1'" - ] - elif len(ncbi_result) == 0: - error += [ - "The result from fetching NCBI genome data has zero length. Please check your internet connection!" - ] - else: - ncbi_genome = [ - i.split("\t") - for i in ncbi_result.split("\n") - if not (i.startswith("New version") or i.startswith("Warning")) - ] - ncbi_genome = dict(zip(ncbi_genome[0], ncbi_genome[1])) - log += ["Found the following genome(s):"] - for k in ncbi_genome.keys(): - log += ["{0}: {1}".format(k, ncbi_genome.get(k))] - refseq_id = ncbi_genome.get("Assembly Accession") - if not refseq_id.startswith("GCF_"): - error += ["The RefSeq ID '{0}' has no valid format.".format(refseq_id)] - ncbi_command = ( - f"datasets download genome accession {refseq_id}" - + f" --filename {output_path}/database.zip --include genome,gff3; " - + f"cd {output_path}; unzip database.zip; rm database.zip" - ) - copy_command = ( - f"cp {output_path}/ncbi_dataset/data/{refseq_id}/*.fna {output_fasta}; " - + f"cp {output_path}/ncbi_dataset/data/{refseq_id}/genomic.gff {output_gff}" - ) - index_command = f"samtools faidx {output_fasta}" - cleanup_command = ( - f"rm -rf {output_path}/ncbi_dataset; " + f"rm -f {output_path}/README.md" - ) - str_out = getoutput(ncbi_command) - str_cp = getoutput(copy_command) - str_cl = getoutput(cleanup_command) - # import and check files - fasta, log, error = check_fasta(output_fasta, log, error) - index_out = getoutput(index_command) - gff, log, error = check_gff(output_gff, log, error) - # write cleaned gff file - with open(output_gff, "w") as gff_out: - GFF.write(gff, gff_out) - -elif input_database.lower() == "manual": - if not path.exists(input_fasta): - error += ["The parameter 'fasta' is not a valid path to a FASTA file"] - elif not path.exists(input_gff): - error += ["The parameter 'gff' is not a valid path to a GFF/GTF file"] - else: - # import and check files - fasta, log, error = check_fasta(input_fasta, log, error) - gff, log, error = check_gff(input_gff, log, error) - # export fasta and gff files - with open(output_fasta, "w") as fasta_out: - fasta_out.write(fasta) - with open(output_gff, "w") as gff_out: - GFF.write(gff, gff_out) -else: - error += ["The parameter 'database' is none of 'ncbi', 'manual'"] - -# print error/log messages -if error: - print("\n".join(error)) - raise ValueError( - "Location or format of the supplied genome files was not correct, quitting" - ) -else: - log += ["Module finished successfully\n"] - log = ["GET_GENOME: " + i for i in log] - with open(output_log, "w") as log_file: - log_file.write("\n".join(log)) From 83ebbf5e5eaee1f4ef12fa90d4c33869e8a30e01 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Mon, 6 Oct 2025 15:59:26 +0200 Subject: [PATCH 07/27] fix: replaced samtools flagstat with wrapper --- workflow/rules/qc.smk | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/workflow/rules/qc.smk b/workflow/rules/qc.smk index ae3fe28..270a94d 100644 --- a/workflow/rules/qc.smk +++ b/workflow/rules/qc.smk @@ -22,15 +22,13 @@ rule alignment_stats: "results/{step}/{sample}.bam", output: "results/qc/{step}/{sample}.flagstat", - conda: - "../envs/samtools.yml" log: "results/qc/{step}/{sample}_flagstat.log", message: "--- Generate mapping statistics of BAM file using samtools." threads: int(workflow.cores * 0.2) - shell: - "samtools flagstat -@ {threads} {input} > {output} 2> {log}" + wrapper: + "v7.5.0/bio/samtools/flagstat" rule qc_biotype_barplot: From 6b9376bd00e9499d4d2e843898b196a83835db9b Mon Sep 17 00:00:00 2001 From: m-jahn Date: Tue, 7 Oct 2025 10:59:29 +0200 Subject: [PATCH 08/27] fix: UMI extraction and dedup for all protocols --- .test/config/samplesheet/samples_neb_umi.tsv | 6 +- .test/config/samplesheet/samples_nextflex.tsv | 6 +- resources/protocols/rnaseq_neb_umi.yml | 58 ++++++---------- resources/protocols/rnaseq_nextflex.yml | 68 ++++++------------- workflow/rules/common.smk | 12 +--- workflow/rules/dedup.smk | 37 ++++++---- workflow/rules/mapping.smk | 2 +- workflow/rules/qc.smk | 4 +- workflow/rules/quantification.smk | 6 +- workflow/rules/trim.smk | 2 +- workflow/scripts/extract_umis.py | 33 ++++----- 11 files changed, 93 insertions(+), 141 deletions(-) diff --git a/.test/config/samplesheet/samples_neb_umi.tsv b/.test/config/samplesheet/samples_neb_umi.tsv index a480d41..ff762fc 100644 --- a/.test/config/samplesheet/samples_neb_umi.tsv +++ b/.test/config/samplesheet/samples_neb_umi.tsv @@ -1,3 +1,3 @@ -sample condition replicate experiment fq1 fq2 read1 -RNA-1 RNA 1 rnaseq_neb_umi data/rnaseq_neb_umi/RNA-1_R1.fastq.gz data/rnaseq_neb_umi/RNA-1_R2.fastq.gz data/rnaseq_neb_umi/RNA-1_UMI.fastq.gz -RNA-2 RNA 2 rnaseq_neb_umi data/rnaseq_neb_umi/RNA-2_R1.fastq.gz data/rnaseq_neb_umi/RNA-2_R2.fastq.gz data/rnaseq_neb_umi/RNA-2_UMI.fastq.gz +sample condition replicate read1 read2 readumi +RNA-1 RNA 1 data/rnaseq_neb_umi/RNA-1_R1.fastq.gz data/rnaseq_neb_umi/RNA-1_R2.fastq.gz data/rnaseq_neb_umi/RNA-1_UMI.fastq.gz +RNA-2 RNA 2 data/rnaseq_neb_umi/RNA-2_R1.fastq.gz data/rnaseq_neb_umi/RNA-2_R2.fastq.gz data/rnaseq_neb_umi/RNA-2_UMI.fastq.gz diff --git a/.test/config/samplesheet/samples_nextflex.tsv b/.test/config/samplesheet/samples_nextflex.tsv index ae546d2..9cdb7b5 100644 --- a/.test/config/samplesheet/samples_nextflex.tsv +++ b/.test/config/samplesheet/samples_nextflex.tsv @@ -1,3 +1,3 @@ -sample condition replicate experiment fq1 fq2 read1 -RNA-1 RNA 1 rnaseq_nextflex data/rnaseq_nextflex/RNA-1_R1.fastq.gz RNA-1_R2.fastq.gz - -RNA-2 RNA 2 rnaseq_nextflex data/rnaseq_nextflex/RNA-2_R1.fastq.gz RNA-2_R2.fastq.gz - +sample condition replicate read1 read2 readumi +RNA-1 RNA 1 data/rnaseq_nextflex/RNA-1_R1.fastq.gz data/rnaseq_nextflex/RNA-1_R2.fastq.gz - +RNA-2 RNA 2 data/rnaseq_nextflex/RNA-2_R1.fastq.gz data/rnaseq_nextflex/RNA-2_R2.fastq.gz - diff --git a/resources/protocols/rnaseq_neb_umi.yml b/resources/protocols/rnaseq_neb_umi.yml index 636b7a5..2f5bede 100644 --- a/resources/protocols/rnaseq_neb_umi.yml +++ b/resources/protocols/rnaseq_neb_umi.yml @@ -11,45 +11,33 @@ get_genome: "RefSeq": "gene", "RefSeq": "pseudogene", "RefSeq": "CDS", - "Protein Homology": "CDS" + "Protein Homology": "CDS", ] extract_features: biotypes: ["protein_coding", "pseudogene", "ncRNA", "rRNA", "tRNA"] +umi_extraction: + method: "" + pattern: "" umi_dedup: "--edit-distance-threshold=0" -cutadapt: - read1_adapter: "AGATCGGAAGAGCACACGTCTGAACTCCAGTCA" - read2_adapter: "AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT" - default: - [ - "-q 10 ", - "-m 18 ", - "--overlap=3" - ] +fastp: + extra: "-M 10 -l 18 --adapter_sequence AGATCGGAAGAGCACACGTCTGAACTCCAGTCA --adapter_sequence_r2 AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT" star: - index: Null - genomeSAindexNbases: 9 - multi: 10 - sam_multi: 1 - intron_max: 1 - default: + index: "--genomeSAindexNbases 9" + extra: [ - "--readFilesCommand zcat ", - "--outSAMstrandField None ", - "--outSAMattributes All ", - "--outSAMattrIHstart 0 ", - "--outFilterType Normal ", - "--outFilterMultimapScoreRange 1 ", - "-o STARmappings ", - "--outSAMtype BAM Unsorted ", - "--outStd BAM_Unsorted ", + "--outFilterMultimapNmax 10 ", + "--outSAMmultNmax 1", "--outMultimapperOrder Random ", - "--alignEndsType EndToEnd", ] +samtools: + sort: "" + index: "" + feature_counts: defaults: [ @@ -58,18 +46,16 @@ feature_counts: "-g locus_tag", "-M", "--fracOverlap 0.2", - "--largestOverlap" + "--largestOverlap", ] deeptools: - bin_size: 1 - normalize: "CPM" - defaults: "--exactScaling" - paired_end: "--extendReads" + genome_size: 2000000 + extra: "--binSize 1 --normalizeUsing CPM --exactScaling --extendReads" + +fastqc: + extra: "--quiet --nogroup" multiqc: - config: "config/multiqc_config.yml" - defaults: ["--dirs-depth 2 ", - "--exclude general_stats ", - "--force ", - "--dirs"] + config: "config/multiqc_config.yml" + extra: "--dirs" diff --git a/resources/protocols/rnaseq_nextflex.yml b/resources/protocols/rnaseq_nextflex.yml index 9806fc2..0f44010 100644 --- a/resources/protocols/rnaseq_nextflex.yml +++ b/resources/protocols/rnaseq_nextflex.yml @@ -11,7 +11,7 @@ get_genome: "RefSeq": "gene", "RefSeq": "pseudogene", "RefSeq": "CDS", - "Protein Homology": "CDS" + "Protein Homology": "CDS", ] extract_features: @@ -19,51 +19,25 @@ extract_features: umi_extraction: method: "string" - pattern: - [ - "--bc-pattern=NNNN ", - "--bc-pattern2=NNNN" - ] + pattern: "--bc-pattern=NNNN --bc-pattern2=NNNN" umi_dedup: "--edit-distance-threshold=0" -trunc_fastq: - default: - [ - "-u='-4' ", - "-U='-4'" - ] - -cutadapt: - read1_adapter: "TGGAATTCTCGGGTGCCAAGGAACTCCAGTCAC" - read2_adapter: "GATCGTCGGACTGTAGAACTCTGAACGTGTAGATC" - default: - [ - "-q 10 ", - "-m 22 ", - "--overlap=3" - ] +fastp: + extra: "-M 10 -l 18 --trim_tail1=4 --trim_tail2=4" star: - index: Null - genomeSAindexNbases: 9 - multi: 10 - sam_multi: 1 - intron_max: 1 - default: + index: "--genomeSAindexNbases 9" + extra: [ - "--readFilesCommand zcat ", - "--outSAMstrandField None ", - "--outSAMattributes All ", - "--outSAMattrIHstart 0 ", - "--outFilterType Normal ", - "--outFilterMultimapScoreRange 1 ", - "-o STARmappings ", - "--outSAMtype BAM Unsorted ", - "--outStd BAM_Unsorted ", + "--outFilterMultimapNmax 10 ", + "--outSAMmultNmax 1", "--outMultimapperOrder Random ", - "--alignEndsType EndToEnd", ] +samtools: + sort: "" + index: "" + feature_counts: defaults: [ @@ -72,18 +46,16 @@ feature_counts: "-g locus_tag", "-M", "--fracOverlap 0.2", - "--largestOverlap" + "--largestOverlap", ] deeptools: - bin_size: 1 - normalize: "CPM" - defaults: "--exactScaling" - paired_end: "--extendReads" + genome_size: 2000000 + extra: "--binSize 1 --normalizeUsing CPM --exactScaling --extendReads" + +fastqc: + extra: "--quiet --nogroup" multiqc: - config: "config/multiqc_config.yml" - defaults: ["--dirs-depth 2 ", - "--exclude general_stats ", - "--force ", - "--dirs"] + config: "config/multiqc_config.yml" + extra: "--dirs" diff --git a/workflow/rules/common.smk b/workflow/rules/common.smk index d8173b7..cb3da8f 100644 --- a/workflow/rules/common.smk +++ b/workflow/rules/common.smk @@ -40,13 +40,6 @@ def is_paired_end(): raise ValueError(msg) -# test presence of read1, read2, and umi_read -def get_reads(): - reads = ["read1", "read2"] if is_paired_end() else ["read1"] - reads += ["readumi"] if samples["readumi"].notna().all() else [] - return reads - - # get fastq files def get_fastq(wildcards): file = Path(samples.loc[wildcards["sample"]][wildcards["read"]]) @@ -60,9 +53,10 @@ def get_fastq(wildcards): # get pairs of fastq files for fastp def get_fastq_pairs(wildcards): return expand( - "results/umi_extract/{sample}_{read}.fastq.gz", + "results/umi_extract{separate}/{sample}_{read}.fastq.gz", + separate="_separate" if samples["readumi"].notna().all() else "", sample=wildcards.sample, - read=get_reads(), + read=["read1", "read2"] if is_paired_end() else ["read1"], ) diff --git a/workflow/rules/dedup.smk b/workflow/rules/dedup.smk index 8d9f781..e120d72 100644 --- a/workflow/rules/dedup.smk +++ b/workflow/rules/dedup.smk @@ -24,7 +24,7 @@ rule umi_extract_standard: conda: "../envs/umitools.yml" message: - "--- Extracting UMIs." + "--- Extracting UMIs from read." params: method=config["umi_extraction"]["method"], pattern=config["umi_extraction"]["pattern"], @@ -48,19 +48,26 @@ rule umi_extract_standard: """ -# rule umi_extract_separate: -# input: -# "results/get_fastq/{sample}_{read}.fastq.gz", -# output: -# "results/umi_extract/{sample}_{read}.fastq.gz", -# conda: -# "../envs/umitools.yml" -# log: -# "results/umi_extract/log/{sample}_{read}.log", -# message: -# """--- Extracting UMIs.""" -# script: -# "../scripts/extract_umis.py" +rule umi_extract_separate: + input: + fq1="results/get_fastq/{sample}_read1.fastq.gz", + fq2="results/get_fastq/{sample}_read2.fastq.gz" if is_paired_end() else "", + fqumi="results/get_fastq/{sample}_readumi.fastq.gz", + output: + fq1="results/umi_extract_separate/{sample}_read1.fastq.gz", + fq2=( + "results/umi_extract_separate/{sample}_read2.fastq.gz" + if is_paired_end() + else "" + ), + conda: + "../envs/umitools.yml" + log: + "results/umi_extract_separate/log/{sample}.log", + message: + "--- Extracting UMIs from separate Fastq file." + script: + "../scripts/extract_umis.py" rule umi_dedup_pe: @@ -73,7 +80,7 @@ rule umi_dedup_pe: conda: "../envs/umitools.yml" message: - """--- UMI tools deduplication.""" + "--- UMI tools deduplication." params: tmp="results/deduplicated/sort_{sample}_tmp", default=config["umi_dedup"], diff --git a/workflow/rules/mapping.smk b/workflow/rules/mapping.smk index 9479716..b1e94ab 100644 --- a/workflow/rules/mapping.smk +++ b/workflow/rules/mapping.smk @@ -15,7 +15,7 @@ rule get_genome: gff="results/genome/genome.gff", fai="results/genome/genome.fasta.fai", message: - """--- Parsing genome GFF and FASTA files.""" + "--- Parsing genome GFF and FASTA files." params: database=config["get_genome"]["database"], assembly=config["get_genome"]["assembly"], diff --git a/workflow/rules/qc.smk b/workflow/rules/qc.smk index 270a94d..3a30454 100644 --- a/workflow/rules/qc.smk +++ b/workflow/rules/qc.smk @@ -42,7 +42,7 @@ rule qc_biotype_barplot: log: path="results/qc/biotypes/log/extract_biotype_data.log", message: - """--- Generate multiqc barplot data for biotype distribution.""" + "--- Generate multiqc barplot data for biotype distribution." script: "../scripts/plot_biotypes.py" @@ -53,7 +53,7 @@ rule get_conda_envs: conda: "../envs/base.yml" message: - """--- Extract software version from conda envs.""" + "--- Extract software version from conda envs." params: conda_files=" ".join(get_conda_envs_files()), conda_envs_log=workflow.source_path("../../resources/conda_envs/conda_envs.log"), diff --git a/workflow/rules/quantification.smk b/workflow/rules/quantification.smk index ee224c9..ce35008 100644 --- a/workflow/rules/quantification.smk +++ b/workflow/rules/quantification.smk @@ -40,7 +40,7 @@ rule quantify_biotypes: conda: "../envs/feature_counts.yml" message: - """--- Quantify biotpyes with subread's featureCount.""" + "--- Quantify biotpyes with subread's featureCount." log: path="results/qc/biotypes/{sample}.counts.log", threads: min(max(1, int(workflow.cores * 0.2)), 64) # assign 20% of max cores @@ -73,7 +73,7 @@ rule combine_count_tables: conda: "../envs/quantify_biotypes.yml" message: - """--- Combine count tables for all samples.""" + "--- Combine count tables for all samples." log: path="results/quantify_biotypes/log/merge_counts.log", params: @@ -91,7 +91,7 @@ rule summarize_biotypes: conda: "../envs/quantify_biotypes.yml" message: - """--- Extract fraction of biotypes for all samples.""" + "--- Extract fraction of biotypes for all samples." log: path="results/quantify_biotypes/log/summarize_biotypes.log", params: diff --git a/workflow/rules/trim.smk b/workflow/rules/trim.smk index ae72944..5b53905 100644 --- a/workflow/rules/trim.smk +++ b/workflow/rules/trim.smk @@ -6,7 +6,7 @@ rule fastp: json="results/fastp/{sample}.json", trimmed=expand( "results/fastp/{{sample}}_{read}.fastq.gz", - read=get_reads(), + read=["read1", "read2"] if is_paired_end() else ["read1"], ), log: "results/fastp/{sample}.log", diff --git a/workflow/scripts/extract_umis.py b/workflow/scripts/extract_umis.py index 2f10ce4..de64f38 100644 --- a/workflow/scripts/extract_umis.py +++ b/workflow/scripts/extract_umis.py @@ -33,34 +33,27 @@ def extract_umi(R1, R2, output, log, error): # set input/output parameters # --------------------------- -is_paired_end = False - -# paired-end -if len(snakemake.input) > 2: - is_paired_end = True - file_r1 = snakemake.input[0] - file_r2 = snakemake.input[1] - file_umis = snakemake.input[2] - output_r1 = snakemake.output["R1"] - output_r2 = snakemake.output["R2"] -# single-end -else: - file_r1 = snakemake.input[0] - file_umis = snakemake.input[1] - output_r1 = snakemake.output["fastq"] - - -output_log = snakemake.log["path"] +output_log = snakemake.log[0] log = [] error = [] -if is_paired_end: +# paired-end +if len(snakemake.input) > 2: + file_r1 = snakemake.input["fq1"] + file_r2 = snakemake.input["fq2"] + file_umis = snakemake.input["fqumi"] + output_r1 = snakemake.output["fq1"] + output_r2 = snakemake.output["fq2"] log += ["Processing R1 ..."] extract_umi(R1=file_r1, R2=file_umis, output=output_r1, log=log, error=error) - # need to write R2 as well log += ["Processing R2 ..."] extract_umi(R1=file_r2, R2=file_umis, output=output_r2, log=log, error=error) +# single-end else: + is_paired_end = False + file_r1 = snakemake.input["fq1"] + file_umis = snakemake.input["fqumi"] + output_r1 = snakemake.output["fq1"] extract_umi(R1=file_r1, R2=file_umis, output=output_r1, log=log, error=error) From d516912471bc93d4d3984c69fe66548eda63eec6 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Tue, 7 Oct 2025 11:00:26 +0200 Subject: [PATCH 09/27] fix: added fastp and simplified fastqc input for multiqc --- .test/config/multiqc_config.yml | 23 ++++++----------------- config/multiqc_config.yml | 23 ++++++----------------- workflow/rules/common.smk | 4 ++++ 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/.test/config/multiqc_config.yml b/.test/config/multiqc_config.yml index dcf1a68..1ad40a8 100644 --- a/.test/config/multiqc_config.yml +++ b/.test/config/multiqc_config.yml @@ -24,21 +24,10 @@ remove_sections: - samtools-stats module_order: + - fastqc + - fastp - star - - cutadapt - - fastqc: - name: "FastQC (trimmed)" - anchor: "fastqc_trimmed" - info: "This section of the report shows FastQC results after adapter trimming." - target: "" - path_filters: - - "*/clipped_reads/*_fastqc.zip" - - fastqc: - name: "FastQC (raw)" - anchor: "fastqc_raw" - path_filters: - - "*/raw_reads/*_fastqc.zip" - -report_section_order: - fastqc_raw: - before: fastqc_trimmed + - umitools + - featurecounts + - samtools + - biotype_dist diff --git a/config/multiqc_config.yml b/config/multiqc_config.yml index dcf1a68..1ad40a8 100644 --- a/config/multiqc_config.yml +++ b/config/multiqc_config.yml @@ -24,21 +24,10 @@ remove_sections: - samtools-stats module_order: + - fastqc + - fastp - star - - cutadapt - - fastqc: - name: "FastQC (trimmed)" - anchor: "fastqc_trimmed" - info: "This section of the report shows FastQC results after adapter trimming." - target: "" - path_filters: - - "*/clipped_reads/*_fastqc.zip" - - fastqc: - name: "FastQC (raw)" - anchor: "fastqc_raw" - path_filters: - - "*/raw_reads/*_fastqc.zip" - -report_section_order: - fastqc_raw: - before: fastqc_trimmed + - umitools + - featurecounts + - samtools + - biotype_dist diff --git a/workflow/rules/common.smk b/workflow/rules/common.smk index cb3da8f..8bf2b66 100644 --- a/workflow/rules/common.smk +++ b/workflow/rules/common.smk @@ -90,6 +90,10 @@ def get_conda_envs_files(): def get_multiqc_input(wildcards): inputs = [] + inputs += expand( + "results/fastp/{sample}.json", + sample=samples.index, + ) inputs += expand( "results/fastqc/{sample}_{read}_fastqc.{ext}", sample=samples.index, From 09d3fa5fc154c30933cc9c7f9dd54c603fe166db Mon Sep 17 00:00:00 2001 From: m-jahn Date: Tue, 7 Oct 2025 12:04:05 +0200 Subject: [PATCH 10/27] fix: update docs and removing redundancies --- README.md | 2 +- config/README.md | 34 ++++++++++++------------ workflow/Snakefile | 65 ++++++++++------------------------------------ 3 files changed, 31 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 0d72ea1..ea236a6 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ This workflow is a best-practice workflow for the processing of short read seque 1. Using automatic download from NCBI with a `RefSeq` ID 2. Using user-supplied files 2. Check quality of input sequencing data ([FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) -3. Cut adapters and filter by length and/or sequencing quality score ([Cutadapt](https://cutadapt.readthedocs.io/en/stable/)) +3. Cut adapters and filter by length and/or sequencing quality score ([fastp](https://github.com/OpenGene/fastp)) 4. Identify unique molecular identifier (UMI, [UMI-tools](https://umi-tools.readthedocs.io/en/latest/)) 5. Map reads to the reference genome ([STAR aligner](https://github.com/alexdobin/STAR)) 6. Sort and index aligned rnaseq data ([Samtools](http://www.htslib.org/)) diff --git a/config/README.md b/config/README.md index 2f3a025..75ead22 100644 --- a/config/README.md +++ b/config/README.md @@ -6,7 +6,7 @@ This workflow is a best-practice workflow for the processing of short read seque 1. Using automatic download from NCBI with a `RefSeq` ID 2. Using user-supplied files 2. Check quality of input sequencing data ([FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) -3. Cut adapters and filter by length and/or sequencing quality score ([Cutadapt](https://cutadapt.readthedocs.io/en/stable/)) +3. Cut adapters and filter by length and/or sequencing quality score ([fastp](https://github.com/OpenGene/fastp)) 4. Identify unique molecular identifier (UMI, [UMI-tools](https://umi-tools.readthedocs.io/en/latest/)) 5. Map reads to the reference genome ([STAR aligner](https://github.com/alexdobin/STAR)) 6. Sort and index aligned rnaseq data ([Samtools](http://www.htslib.org/)) @@ -34,26 +34,24 @@ Important requirements when using custom `*.fasta` and `*.gff` files: RNA sequencing data in `*.fastq.gz` format. The currently supported input data are **second generation reads**. Input data files are supplied via a mandatory table, whose location is indicated in the `config.yml` file (default: `samples.tsv`). The sample sheet has the following layout: -| sample | condition | replicate | experiment | fq1 | fq2 | read1 | -| ------ | --------- | --------- | ------------------- | ----------------- | ----------------- | ------ | -| RNA-1 | RNA | 1 | rnaseq_mpusp_custom | RNA-1_R1.fastq.gz | RNA-1_R2.fastq.gz | \- | -| RNA-2 | RNA | 2 | rnaseq_mpusp_custom | RNA-2_R2.fastq.gz | RNA-2_R2.fastq.gz | \- | +| sample | condition | replicate | read1 | read2 | readumi | +| ------ | --------- | --------- | ----------------- | ----------------- | ------- | +| RNA-1 | RNA | 1 | RNA-1_R1.fastq.gz | RNA-1_R2.fastq.gz | \- | +| RNA-2 | RNA | 2 | RNA-2_R2.fastq.gz | RNA-2_R2.fastq.gz | \- | Some configuration parameters of the pipeline may be specific for your data and library preparation protocol. The options should be adjusted in the `config.yml` file. -Currently, we support example configurations for three different sequencing protocols, _i.e._ `rnaseq_nextflex`, `rnaseq_neb_umi`and `rnseq_mpusp_custom`. These example protocols can be found in `resources/protocols/`. +Configuration files for different sequencing protocols can be found in `resources/protocols/`. +Currently, you may find protocols for _i.e._ `rnaseq_nextflex`, `rnaseq_neb_umi` and a custom protocol `rnaseq_mpusp_custom`. ### Output -### Output - -| Output File/Folder | Description | -| ---------------------------- | ------------------------------------------------------------------------ | -| `results/clipped/` | Adapter-trimmed and quality-filtered FASTQ files. | -| `results/deduplicated/` | UMI-processed FASTQ/BAM files and UMI statistics. | -| `results/genome/` | Downloaded or user-supplied reference genome and annotation files. | -| `results/mapped/` | Aligned reads in BAM format (sorted and indexed). | -| `results/qc/` | Quality control reports for raw and processed reads (FastQC HTML files). | -| `results/deeptools/` | CPM-normalized coverage files (bigWig format). | -| `results/quantify_biotypes/` | Gene/feature count tables (tab-delimited text files). | -| `results/report/` | MultiQC report aggregating QC metrics from all steps. | +| Output File/Folder | Description | +| ---------------------------- | ---------------------------------------------------------------------------- | +| `results/genome/` | Downloaded or user-supplied reference genome and annotation files. | +| `results/fastp/` | Adapter-trimmed and quality-filtered FASTQ files. | +| `results/mapped/` | Aligned reads in BAM format, coverage in BigWig format | +| `results/deduplicated/` | Aligned and UMI-deduplicated reads in BAM format, coverage in BigWig format. | +| `results/qc/` | Quality control reports for raw and processed reads (FastQC HTML files). | +| `results/quantify_biotypes/` | Gene/feature count tables (tab-delimited text files). | +| `results/multiqc/` | MultiQC report aggregating QC metrics from all steps. | diff --git a/workflow/Snakefile b/workflow/Snakefile index b30b81b..d761dd9 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -1,31 +1,12 @@ -# ----------------------------------------------------- # -# A Snakemake workflow for the preprocessing of short # -# read RNAseq data in bacteria. # -# # -# Authors: Rina Ahmed-Begrich # -# Date: 2025-01-02 # -# License: MIT # -# ----------------------------------------------------- # import os - -__author__ = "Rina Ahmed-Begrich" -__year__ = "2025" - -bold = "\033[1m" -green = "\033[92m" -cyan = "\033[36m" -end = "\033[0m" - -msg = f"""{cyan}Bacterial-rnaseq-preprocessing: A Snakemake workflow -for the preprocessing of short read RNAseq data in bacteria.{end} - -""" - -epilog = f"""{cyan}Written by {__author__}, -Max Planck Unit for the Science of Pathogens. Copyright (c) {__year__}. -Copyright Holder All Rights Reserved.{end} - +epilog = """ +\033[36m +Bacterial-rnaseq-preprocessing: A Snakemake workflow +for the preprocessing of short read RNAseq data in bacteria. +--- +Max Planck Unit for the Science of Pathogens. Copyright (c) 2025. +\033[0m """ @@ -37,45 +18,27 @@ configfile: "config/config.yml" # load rules # ----------------------------------------------------- include: "rules/common.smk" -include: "rules/qc.smk" -include: "rules/trim.smk" +include: "rules/coverage.smk" include: "rules/dedup.smk" include: "rules/mapping.smk" +include: "rules/qc.smk" include: "rules/quantification.smk" -include: "rules/coverage.smk" +include: "rules/trim.smk" onstart: - print("\n--- Analysis started...\n") - print() - print("--- Analysis parameters --------------------------------------------\n") - print(f"Current working directory: {os.getcwd()}") - print(f"Output directory: {os.path.join(os.getcwd(), 'results')}") - print() - print(f"RNAseq samples: {list(samples.index)}") - print() + print("\n--- Analysis started ---\n") + print(f"RNAseq samples: {list(samples.index)}\n\n") onsuccess: - print() - print(msg) + print("\n--- Workflow finished successfully! ---\n") print(epilog) - print("--- Workflow finished, no error! -----------------------------------") - print() - debug = os.path.join(os.getcwd(), "results/workflow.log") - shell( - "cat {log} > {debug} && echo -e '\nWorkflow finished, no error!\n' >> {debug}" - ) onerror: - print() - print(msg) + print("\n--- An error occurred, see the snakemake log ---\n") print(epilog) - print("--- An error occurred! ---------------------------------------------") - print() - error = os.path.join(os.getcwd(), "results/error.log") - shell("cat {log} > {error} && echo -e '\nAn error occurred!' >> {error}") # target rules From 4be847d11f2866bd6c17644f5096f34a9225fae6 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Tue, 7 Oct 2025 13:23:00 +0200 Subject: [PATCH 11/27] feat: added schemas, closes #6, closes #7 --- config/schemas/config.schema.yml | 88 +++++++++++++++++++++++++++++++ config/schemas/samples.schema.yml | 28 ++++++++++ workflow/rules/common.smk | 14 ++--- 3 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 config/schemas/config.schema.yml create mode 100644 config/schemas/samples.schema.yml diff --git a/config/schemas/config.schema.yml b/config/schemas/config.schema.yml new file mode 100644 index 0000000..91e7402 --- /dev/null +++ b/config/schemas/config.schema.yml @@ -0,0 +1,88 @@ +$schema: "https://json-schema.org/draft/2020-12/schema" +description: config file structure +properties: + samplesheet: + type: string + libtype: + type: string + get_genome: + properties: + database: + type: [string, "null"] + assembly: + type: [string, "null"] + fasta: + type: [string, "null"] + gff: + type: [string, "null"] + gff_source_type: + type: array + extract_features: + properties: + biotypes: + type: array + items: + type: string + umi_extraction: + properties: + method: + type: string + enum: ["regex", "string"] + pattern: + type: string + umi_dedup: + type: string + fastp: + properties: + extra: + type: string + star: + properties: + index: + type: [string, "null"] + extra: + type: array + items: + type: string + samtools: + properties: + sort: + type: string + index: + type: string + feature_counts: + properties: + defaults: + type: array + items: + type: string + deeptools: + properties: + genome_size: + type: number + extra: + type: string + fastqc: + properties: + extra: + type: string + multiqc: + properties: + config: + type: string + extra: + type: string +required: + - samplesheet + - libtype + - get_genome + - extract_features + - umi_extraction + - umi_dedup + - fastp + - star + - samtools + - feature_counts + - deeptools + - fastqc + - multiqc diff --git a/config/schemas/samples.schema.yml b/config/schemas/samples.schema.yml new file mode 100644 index 0000000..c642bbd --- /dev/null +++ b/config/schemas/samples.schema.yml @@ -0,0 +1,28 @@ +$schema: "https://json-schema.org/draft/2020-12/schema" +description: an entry in the sample sheet +properties: + sample: + type: string + description: sample name/identifier + condition: + type: string + description: sample condition that will be compared during differential analysis + replicate: + type: number + default: 1 + description: consecutive numbers representing multiple replicates of one condition + read1: + type: string + description: names of fastq files, read 1 + read2: + type: string + description: names of fastq files, read 2 (optional) + readumi: + type: string + description: names of fastq files, providing UMI (optional) + +required: + - sample + - condition + - replicate + - read1 diff --git a/workflow/rules/common.smk b/workflow/rules/common.smk index 8bf2b66..4d35991 100644 --- a/workflow/rules/common.smk +++ b/workflow/rules/common.smk @@ -1,6 +1,6 @@ -import os import pandas as pd from snakemake.logging import logger +from snakemake.utils import validate from pathlib import Path @@ -23,6 +23,11 @@ wildcard_constraints: read="|".join(["read1", "read2", "readumi"]), +# validate sample sheet and config file +validate(samples, schema="../../config/schemas/samples.schema.yml") +validate(config, schema="../../config/schemas/config.schema.yml") + + # helpers # ----------------------------------------------------- # determine input type, all entries must be either single or paired end @@ -76,12 +81,9 @@ def get_stats_input(wildcards): # returns path to conda envs files def get_conda_envs_files(): - wf_dir = os.path.abspath(workflow.basedir) - envs = [] + wf_dir = Path(workflow.basedir).absolute() / "envs" try: - envs += expand( - os.path.join(wf_dir, "envs", "{envs}"), envs=os.listdir(f"{wf_dir}/envs") - ) + envs = [str(i) for i in wf_dir.iterdir()] except FileNotFoundError: msg = "No conda environments found in the 'envs' directory." logger.error(msg) From 74ca2ca42025afade7ea368571f7b0e984538de8 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Tue, 7 Oct 2025 14:13:01 +0200 Subject: [PATCH 12/27] fix: update for config was missing --- config/config.yml | 58 ++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/config/config.yml b/config/config.yml index a9a3478..4fdc0e4 100644 --- a/config/config.yml +++ b/config/config.yml @@ -11,45 +11,33 @@ get_genome: "RefSeq": "gene", "RefSeq": "pseudogene", "RefSeq": "CDS", - "Protein Homology": "CDS" + "Protein Homology": "CDS", ] extract_features: biotypes: ["protein_coding", "pseudogene", "ncRNA", "rRNA", "tRNA"] +umi_extraction: + method: "" + pattern: "" umi_dedup: "--edit-distance-threshold=0" -cutadapt: - read1_adapter: "AGATCGGAAGAGCACACGTCTGAACTCCAGTCA" - read2_adapter: "AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT" - default: - [ - "-q 10 ", - "-m 18 ", - "--overlap=3" - ] +fastp: + extra: "-M 10 -l 18 --adapter_sequence AGATCGGAAGAGCACACGTCTGAACTCCAGTCA --adapter_sequence_r2 AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT" star: - index: Null - genomeSAindexNbases: 9 - multi: 10 - sam_multi: 1 - intron_max: 1 - default: + index: "--genomeSAindexNbases 9" + extra: [ - "--readFilesCommand zcat ", - "--outSAMstrandField None ", - "--outSAMattributes All ", - "--outSAMattrIHstart 0 ", - "--outFilterType Normal ", - "--outFilterMultimapScoreRange 1 ", - "-o STARmappings ", - "--outSAMtype BAM Unsorted ", - "--outStd BAM_Unsorted ", + "--outFilterMultimapNmax 10 ", + "--outSAMmultNmax 1", "--outMultimapperOrder Random ", - "--alignEndsType EndToEnd", ] +samtools: + sort: "" + index: "" + feature_counts: defaults: [ @@ -58,18 +46,16 @@ feature_counts: "-g locus_tag", "-M", "--fracOverlap 0.2", - "--largestOverlap" + "--largestOverlap", ] deeptools: - bin_size: 1 - normalize: "CPM" - defaults: "--exactScaling" - paired_end: "--extendReads" + genome_size: 2000000 + extra: "--binSize 1 --normalizeUsing CPM --exactScaling --extendReads" + +fastqc: + extra: "--quiet --nogroup" multiqc: - config: "config/multiqc_config.yml" - defaults: ["--dirs-depth 2 ", - "--exclude general_stats ", - "--force ", - "--dirs"] + config: "config/multiqc_config.yml" + extra: "--dirs" From 861e5fb64814bd38ba97a2637a23a7d71e6a3c9d Mon Sep 17 00:00:00 2001 From: m-jahn Date: Mon, 13 Oct 2025 08:24:30 +0200 Subject: [PATCH 13/27] fix: linting on test dir --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 849c821..9410cd4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: - name: Lint workflow uses: snakemake/snakemake-github-action@v2 with: - directory: . + directory: .test snakefile: workflow/Snakefile args: "--lint" From 237e17b30ce293493b36ca8570eff5b15fe6465c Mon Sep 17 00:00:00 2001 From: m-jahn Date: Mon, 13 Oct 2025 09:16:32 +0200 Subject: [PATCH 14/27] fix: unified all thread defs, enable test run with sufficient threads --- .github/workflows/main.yml | 2 +- workflow/rules/coverage.smk | 2 +- workflow/rules/dedup.smk | 2 +- workflow/rules/mapping.smk | 2 +- workflow/rules/qc.smk | 4 ++-- workflow/rules/quantification.smk | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9410cd4..71aaa0c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -46,7 +46,7 @@ jobs: with: directory: .test snakefile: workflow/Snakefile - args: "--sdm conda --show-failed-logs --cores 2 --conda-cleanup-pkgs cache --all-temp" + args: "--sdm conda --show-failed-logs --cores 4 --conda-cleanup-pkgs cache --all-temp" - name: Test report uses: snakemake/snakemake-github-action@v2 diff --git a/workflow/rules/coverage.smk b/workflow/rules/coverage.smk index 9b9c3e7..7db8bd0 100644 --- a/workflow/rules/coverage.smk +++ b/workflow/rules/coverage.smk @@ -10,7 +10,7 @@ rule deeptools_coverage: bai="results/{step}/{sample}.bam.bai", output: bw="results/{step}/{sample}_cpm_{strand}.bw", - threads: int(workflow.cores * 0.2) + threads: int(workflow.cores * 0.25) params: effective_genome_size=config["deeptools"]["genome_size"], extra=lambda wc: ( diff --git a/workflow/rules/dedup.smk b/workflow/rules/dedup.smk index e120d72..115fee4 100644 --- a/workflow/rules/dedup.smk +++ b/workflow/rules/dedup.smk @@ -88,7 +88,7 @@ rule umi_dedup_pe: path="results/deduplicated/log/{sample}.log", stderr="results/deduplicated/log/{sample}.stderr", stats="results/deduplicated/log/{sample}_umi_stats.txt", - threads: int(workflow.cores * 0.2) # assign 25% of max cores. + threads: int(workflow.cores * 0.25) shell: "umi_tools dedup " "--paired " diff --git a/workflow/rules/mapping.smk b/workflow/rules/mapping.smk index b1e94ab..56c8f68 100644 --- a/workflow/rules/mapping.smk +++ b/workflow/rules/mapping.smk @@ -56,7 +56,7 @@ rule star_mapping: "--- STAR mapping." params: extra=config["star"]["extra"], - threads: int(workflow.cores * 0.2) + threads: int(workflow.cores * 0.25) wrapper: "v7.2.0/bio/star/align" diff --git a/workflow/rules/qc.smk b/workflow/rules/qc.smk index 3a30454..0c27c42 100644 --- a/workflow/rules/qc.smk +++ b/workflow/rules/qc.smk @@ -10,7 +10,7 @@ rule fastqc: "--- Checking fastq files with FastQC" log: "results/fastqc/{sample}.bwa.{read}.log", - threads: 1 + threads: 2 resources: mem_mb=4096, wrapper: @@ -26,7 +26,7 @@ rule alignment_stats: "results/qc/{step}/{sample}_flagstat.log", message: "--- Generate mapping statistics of BAM file using samtools." - threads: int(workflow.cores * 0.2) + threads: int(workflow.cores * 0.25) wrapper: "v7.5.0/bio/samtools/flagstat" diff --git a/workflow/rules/quantification.smk b/workflow/rules/quantification.smk index ce35008..ece772e 100644 --- a/workflow/rules/quantification.smk +++ b/workflow/rules/quantification.smk @@ -43,7 +43,7 @@ rule quantify_biotypes: "--- Quantify biotpyes with subread's featureCount." log: path="results/qc/biotypes/{sample}.counts.log", - threads: min(max(1, int(workflow.cores * 0.2)), 64) # assign 20% of max cores + threads: int(workflow.cores * 0.25) params: defaults=config["feature_counts"]["defaults"], libtype=config["libtype"], From fc38beacc3e48a1a12c3e83c66963b8c3a3b0b82 Mon Sep 17 00:00:00 2001 From: m-jahn Date: Mon, 13 Oct 2025 09:29:40 +0200 Subject: [PATCH 15/27] fix: add number of cores to report --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 71aaa0c..2565579 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -53,4 +53,4 @@ jobs: with: directory: .test snakefile: workflow/Snakefile - args: "--report report.zip" + args: "--cores 4 --report report.zip" From 7b12a441af0c2564d81a1826dd95c7a2c2a67d5e Mon Sep 17 00:00:00 2001 From: jahn Date: Wed, 15 Oct 2025 15:10:00 +0200 Subject: [PATCH 16/27] fix: some fixes to accomodate single-end data and/or no UMIs --- config/schemas/config.schema.yml | 2 +- workflow/rules/dedup.smk | 56 ++++++++++++++++++++----------- workflow/rules/mapping.smk | 2 +- workflow/rules/quantification.smk | 26 ++++++++------ 4 files changed, 53 insertions(+), 33 deletions(-) diff --git a/config/schemas/config.schema.yml b/config/schemas/config.schema.yml index 91e7402..d8feded 100644 --- a/config/schemas/config.schema.yml +++ b/config/schemas/config.schema.yml @@ -27,7 +27,7 @@ properties: properties: method: type: string - enum: ["regex", "string"] + enum: ["regex", "string", "none"] pattern: type: string umi_dedup: diff --git a/workflow/rules/dedup.smk b/workflow/rules/dedup.smk index 115fee4..298c694 100644 --- a/workflow/rules/dedup.smk +++ b/workflow/rules/dedup.smk @@ -17,10 +17,10 @@ rule get_fastq: rule umi_extract_standard: input: fq1="results/get_fastq/{sample}_read1.fastq.gz", - fq2="results/get_fastq/{sample}_read2.fastq.gz" if is_paired_end() else "", + fq2="results/get_fastq/{sample}_read2.fastq.gz" if is_paired_end() else [], output: fq1="results/umi_extract/{sample}_read1.fastq.gz", - fq2="results/umi_extract/{sample}_read2.fastq.gz" if is_paired_end() else "", + fq2="results/umi_extract/{sample}_read2.fastq.gz" if is_paired_end() else [], conda: "../envs/umitools.yml" message: @@ -38,27 +38,34 @@ rule umi_extract_standard: error="results/umi_extract/log/{sample}.err", shell: """ - umi_tools extract \ - --extract-method {params.method} \ - {params.pattern} \ - --stdin {input.fq1} \ - --stdout {output.fq1} \ - {params.read2} \ - --log {log.path} 2> {log.error} + if [[ "{params.method}" != "none" ]]; then + umi_tools extract \ + --extract-method {params.method} \ + {params.pattern} \ + --stdin {input.fq1} \ + --stdout {output.fq1} \ + {params.read2} \ + --log {log.path} 2> {log.error} + else + cp {input.fq1} {output.fq1} + if [[ -n "{params.read2}" ]]; then + cp {input.fq2} {output.fq2} + fi + fi """ rule umi_extract_separate: input: fq1="results/get_fastq/{sample}_read1.fastq.gz", - fq2="results/get_fastq/{sample}_read2.fastq.gz" if is_paired_end() else "", + fq2="results/get_fastq/{sample}_read2.fastq.gz" if is_paired_end() else [], fqumi="results/get_fastq/{sample}_readumi.fastq.gz", output: fq1="results/umi_extract_separate/{sample}_read1.fastq.gz", fq2=( "results/umi_extract_separate/{sample}_read2.fastq.gz" if is_paired_end() - else "" + else [] ), conda: "../envs/umitools.yml" @@ -70,7 +77,7 @@ rule umi_extract_separate: "../scripts/extract_umis.py" -rule umi_dedup_pe: +rule umi_dedup: input: bam="results/mapped/{sample}.bam", bai="results/mapped/{sample}.bam.bai", @@ -82,7 +89,9 @@ rule umi_dedup_pe: message: "--- UMI tools deduplication." params: + method=config["umi_extraction"]["method"], tmp="results/deduplicated/sort_{sample}_tmp", + paired="--paired " if is_paired_end() else "", default=config["umi_dedup"], log: path="results/deduplicated/log/{sample}.log", @@ -90,11 +99,18 @@ rule umi_dedup_pe: stats="results/deduplicated/log/{sample}_umi_stats.txt", threads: int(workflow.cores * 0.25) shell: - "umi_tools dedup " - "--paired " - "{params.default} " - "--stdin={input.bam} " - "--output-stats={log.stats} " - "--log={log.path} 2> {log.stderr} | " - "samtools sort -@ {threads} -O bam -T {params.tmp} -o {output.bam}; " - "samtools index {output.bam}" + """ + if [[ "{params.method}" != "none" ]]; then + umi_tools dedup \ + {params.paired} \ + {params.default} \ + --stdin={input.bam} \ + --output-stats={log.stats} \ + --log={log.path} 2> {log.stderr} | + samtools sort -@ {threads} -O bam -T {params.tmp} -o {output.bam}; + samtools index {output.bam}; + else + samtools sort -@ {threads} -O bam -T {params.tmp} -o {output.bam} {input.bam}; + samtools index {output.bam}; + fi + """ diff --git a/workflow/rules/mapping.smk b/workflow/rules/mapping.smk index 56c8f68..703bb4a 100644 --- a/workflow/rules/mapping.smk +++ b/workflow/rules/mapping.smk @@ -45,7 +45,7 @@ rule star_index: rule star_mapping: input: fq1="results/fastp/{sample}_read1.fastq.gz", - fq2="results/fastp/{sample}_read2.fastq.gz" if is_paired_end() else "", + fq2="results/fastp/{sample}_read2.fastq.gz" if is_paired_end() else [], idx=rules.star_index.output, output: aln="results/mapped/unsorted/{sample}/mapped.bam", diff --git a/workflow/rules/quantification.smk b/workflow/rules/quantification.smk index ece772e..5156e93 100644 --- a/workflow/rules/quantification.smk +++ b/workflow/rules/quantification.smk @@ -47,18 +47,22 @@ rule quantify_biotypes: params: defaults=config["feature_counts"]["defaults"], libtype=config["libtype"], + paired="-p --countReadPairs" if is_paired_end() else "", shell: - "if [ {params.libtype} == 'sense' ]; then " - "libtype=`echo -e '-s 1'`; " - "else libtype=`echo -e '-s 2'`; " - "fi; " - "featureCounts -T {threads} " - "{params.defaults} " - "${{libtype}} " - "-a {input.gtf} " - "-p --countReadPairs " - "-o {output.counts} " - "{input.bam} &> {log.path}" + """ + if [ {params.libtype} == 'sense' ]; then + libtype=`echo -e '-s 1'`; + else + libtype=`echo -e '-s 2'`; + fi; + featureCounts -T {threads} \ + {params.defaults} \ + ${{libtype}} \ + -a {input.gtf} \ + {params.paired} \ + -o {output.counts} \ + {input.bam} &> {log.path} + """ rule combine_count_tables: From e2b75e8a22583f58384fe4264708b45a10e54ff4 Mon Sep 17 00:00:00 2001 From: jahn Date: Thu, 16 Oct 2025 16:39:36 +0200 Subject: [PATCH 17/27] fix: replace hard-coded GFF requirements with flexible rules --- workflow/rules/quantification.smk | 3 ++- workflow/scripts/extract_features.py | 15 +++++++++++--- workflow/scripts/merge_counts.py | 29 ++++++++++++---------------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/workflow/rules/quantification.smk b/workflow/rules/quantification.smk index 5156e93..913f011 100644 --- a/workflow/rules/quantification.smk +++ b/workflow/rules/quantification.smk @@ -8,6 +8,7 @@ rule extract_features: message: "--- Extract selected biotype features from genome annotation." params: + gff_source_types=config["get_genome"]["gff_source_type"], features=config["extract_features"]["biotypes"], log: path="results/extracted_features/log/extract_features.log", @@ -65,7 +66,7 @@ rule quantify_biotypes: """ -rule combine_count_tables: +rule merge_counts: input: counts=expand("results/qc/biotypes/{sample}.counts", sample=samples.index), summary=expand( diff --git a/workflow/scripts/extract_features.py b/workflow/scripts/extract_features.py index 06f3395..5b13b32 100644 --- a/workflow/scripts/extract_features.py +++ b/workflow/scripts/extract_features.py @@ -17,6 +17,14 @@ log = [] error = [] +default_sources = [ + {"RefSeq": "gene"}, + {"RefSeq": "pseudogene"}, + {"RefSeq": "CDS"}, + {"Protein Homology": "CDS"}, +] +gff_source_types = snakemake.params.get("gff_source_types", default_sources) + if not path.exists(input_gff): error += ["The parameter 'gff' is not a valid path to a GFF file"] @@ -25,9 +33,10 @@ try: with open(input_gff, "r") as gff_file: examiner = GFFExaminer() - limits = dict( - gff_source_type=[("RefSeq", "gene"), ("RefSeq", "pseudogene")] - ) + gff_source_type = [] + for i in gff_source_types: + gff_source_type += list(i.items()) + limits = dict(gff_source_type=gff_source_type) for rec in GFF.parse(gff_file, limit_info=limits): sel_features = [] for feat in rec.features: diff --git a/workflow/scripts/merge_counts.py b/workflow/scripts/merge_counts.py index 2d9dc89..297fa0c 100644 --- a/workflow/scripts/merge_counts.py +++ b/workflow/scripts/merge_counts.py @@ -45,23 +45,18 @@ ] biotypes = pd.read_table(gtf, names=gtf_cols, comment="#") - bio_type = ( - pd.DataFrame(biotypes.attributes.apply(lambda x: x.split(";")).to_list()) - .loc[:, 2] - .apply(lambda x: x.lstrip().split(" ")[1].replace('"', "")) - ) - - locus_tag = ( - pd.DataFrame(biotypes.attributes.apply(lambda x: x.split(";")).to_list()) - .loc[:, 3] - .apply(lambda x: x.lstrip().split(" ")[1].replace('"', "")) - ) - - name = ( - pd.DataFrame(biotypes.attributes.apply(lambda x: x.split(";")).to_list()) - .loc[:, 4] - .apply(lambda x: x.lstrip().split(" ")[1].replace('"', "")) - ) + def parse_attrs(s): + pairs = [p.strip() for p in s.split(';') if p.strip()] + d = {} + for p in pairs: + k, v = p.split(' ', 1) + d[k] = v.strip().strip('"') + return d + + attrs = biotypes.attributes.apply(parse_attrs) + bio_type = attrs.map(lambda d: d.get("gene_biotype") or d.get("biotype")) + locus_tag = attrs.map(lambda d: d.get("locus_tag")) + name = attrs.map(lambda d: d.get("Name") or d.get("gene_name")) meta_info = pd.concat([locus_tag, name, bio_type], axis=1, sort=False) meta_info.columns = ["locus_tag", "gene_name", "biotype"] From 78e92d98b9a16234ffe8aac4caae82ff6e618624 Mon Sep 17 00:00:00 2001 From: jahn Date: Thu, 5 Mar 2026 09:45:53 +0100 Subject: [PATCH 18/27] fix: update umitools to latest version --- workflow/envs/umitools.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workflow/envs/umitools.yml b/workflow/envs/umitools.yml index c2d8353..e0d6639 100644 --- a/workflow/envs/umitools.yml +++ b/workflow/envs/umitools.yml @@ -3,7 +3,7 @@ channels: - conda-forge - bioconda dependencies: - - umi_tools=1.1.5 - - samtools=1.21 - - htslib=1.21 + - umi_tools=1.1.6 + - samtools=1.23 + - htslib=1.23 - dnaio=1.2.2 From ab591a99b0d97e87a6148f24943f25a7caea96de Mon Sep 17 00:00:00 2001 From: jahn Date: Thu, 5 Mar 2026 09:48:42 +0100 Subject: [PATCH 19/27] fix: docs to run tests, typo, wrong conf option --- config/README.md | 8 ++++++++ resources/protocols/rnaseq_neb_umi.yml | 2 +- workflow/rules/qc.smk | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/config/README.md b/config/README.md index 75ead22..99ca523 100644 --- a/config/README.md +++ b/config/README.md @@ -44,6 +44,14 @@ Some configuration parameters of the pipeline may be specific for your data and Configuration files for different sequencing protocols can be found in `resources/protocols/`. Currently, you may find protocols for _i.e._ `rnaseq_nextflex`, `rnaseq_neb_umi` and a custom protocol `rnaseq_mpusp_custom`. +To run the workflow with the respective test data for the different protocols, use the following commands: + +```bash +snakemake --sdm conda --cores 12 --directory .test --configfile resources/protocols/rnaseq_mpusp_custom.yml +snakemake --sdm conda --cores 12 --directory .test --configfile resources/protocols/rnaseq_neb_umi.yml +snakemake --sdm conda --cores 12 --directory .test --configfile resources/protocols/rnaseq_nextflex.yml +``` + ### Output | Output File/Folder | Description | diff --git a/resources/protocols/rnaseq_neb_umi.yml b/resources/protocols/rnaseq_neb_umi.yml index 2f5bede..4f3ea9d 100644 --- a/resources/protocols/rnaseq_neb_umi.yml +++ b/resources/protocols/rnaseq_neb_umi.yml @@ -18,7 +18,7 @@ extract_features: biotypes: ["protein_coding", "pseudogene", "ncRNA", "rRNA", "tRNA"] umi_extraction: - method: "" + method: "none" pattern: "" umi_dedup: "--edit-distance-threshold=0" diff --git a/workflow/rules/qc.smk b/workflow/rules/qc.smk index 0c27c42..54e940d 100644 --- a/workflow/rules/qc.smk +++ b/workflow/rules/qc.smk @@ -71,7 +71,7 @@ rule get_software_yaml: input: conda_envs="results/versions/log_conda_envs.txt", output: - yaml="results/versions/rnaseq_preprocessinq_mqc_versions.yml", + yaml="results/versions/rnaseq_preprocessing_mqc_versions.yml", multi_conf="results/multiqc/multiqc_config.yml", conda: "../envs/base.yml" From 99c197ba4cdca6a18f8137dd0551366bc8fe178d Mon Sep 17 00:00:00 2001 From: jahn Date: Thu, 5 Mar 2026 09:50:08 +0100 Subject: [PATCH 20/27] fix: removed unused imports, file handle problem and snakemake import in base env --- workflow/envs/base.yml | 5 +---- workflow/scripts/get_versions.py | 12 +++++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/workflow/envs/base.yml b/workflow/envs/base.yml index 3cfe602..8cd8edb 100644 --- a/workflow/envs/base.yml +++ b/workflow/envs/base.yml @@ -3,7 +3,4 @@ channels: - conda-forge - bioconda dependencies: - - python=3.12.7 - - pandas=2.2.3 - - pyyaml=6.0.2 - - snakemake=8.25.5 + - pyyaml=6.0.3 diff --git a/workflow/scripts/get_versions.py b/workflow/scripts/get_versions.py index 8038b38..ac82ac8 100644 --- a/workflow/scripts/get_versions.py +++ b/workflow/scripts/get_versions.py @@ -7,8 +7,6 @@ # conda envs file. # ----------------------------------------------------------------------------- -import os -import pandas as pd import yaml @@ -33,13 +31,14 @@ error += [f"Error occurred when processing input file '{input_envs}'."] try: - with open(input_conf, "r") as input_conf: - config_dic = yaml.safe_load(input_conf) + with open(input_conf, "r") as config_file: + config_dic = yaml.safe_load(config_file) except IOError: + config_dic = {} error += [f"Error occurred when reading input config '{input_conf}'."] # software info to multiqc config -config_dic["software_versions"] = dict(version_dic) +config_dic["software_versions"] = version_dic try: with open(output_yml, "w") as out_yml: @@ -48,13 +47,12 @@ except IOError: error += [f"Output file '{output_yml}' can not be opened."] - try: with open(out_multi, "w") as multi_yml: log += [f"Writing output YAML file '{out_multi}'..."] yaml.dump(config_dic, multi_yml, default_flow_style=False) except IOError: - error += [f"Output file '{output_yml}' can not be opened."] + error += [f"Output file '{out_multi}' can not be opened."] # print error/log messages From 4fbde14005d4930eb10025cda5952e7e2bbaa04c Mon Sep 17 00:00:00 2001 From: Rina Ahmed-Begrich Date: Thu, 5 Mar 2026 11:22:00 +0100 Subject: [PATCH 21/27] fix: smarter resource declaration, prevents failurewith few cores --- workflow/Snakefile | 6 +++--- workflow/rules/coverage.smk | 2 +- workflow/rules/dedup.smk | 2 +- workflow/rules/mapping.smk | 6 +++--- workflow/rules/qc.smk | 4 ++-- workflow/rules/quantification.smk | 2 +- workflow/rules/trim.smk | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/workflow/Snakefile b/workflow/Snakefile index d761dd9..af7ac13 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -2,10 +2,10 @@ import os epilog = """ \033[36m -Bacterial-rnaseq-preprocessing: A Snakemake workflow -for the preprocessing of short read RNAseq data in bacteria. +Bacterial-rnaseq-processing: A Snakemake workflow +for the processing of short read RNAseq data in bacteria. --- -Max Planck Unit for the Science of Pathogens. Copyright (c) 2025. +Max Planck Unit for the Science of Pathogens. Copyright (c) 2026. \033[0m """ diff --git a/workflow/rules/coverage.smk b/workflow/rules/coverage.smk index 7db8bd0..d0607db 100644 --- a/workflow/rules/coverage.smk +++ b/workflow/rules/coverage.smk @@ -10,7 +10,7 @@ rule deeptools_coverage: bai="results/{step}/{sample}.bam.bai", output: bw="results/{step}/{sample}_cpm_{strand}.bw", - threads: int(workflow.cores * 0.25) + threads: max(1, int(workflow.cores * 0.25)) params: effective_genome_size=config["deeptools"]["genome_size"], extra=lambda wc: ( diff --git a/workflow/rules/dedup.smk b/workflow/rules/dedup.smk index 298c694..93953a2 100644 --- a/workflow/rules/dedup.smk +++ b/workflow/rules/dedup.smk @@ -97,7 +97,7 @@ rule umi_dedup: path="results/deduplicated/log/{sample}.log", stderr="results/deduplicated/log/{sample}.stderr", stats="results/deduplicated/log/{sample}_umi_stats.txt", - threads: int(workflow.cores * 0.25) + threads: max(1, int(workflow.cores * 0.25)) shell: """ if [[ "{params.method}" != "none" ]]; then diff --git a/workflow/rules/mapping.smk b/workflow/rules/mapping.smk index 703bb4a..6f1bd4d 100644 --- a/workflow/rules/mapping.smk +++ b/workflow/rules/mapping.smk @@ -56,7 +56,7 @@ rule star_mapping: "--- STAR mapping." params: extra=config["star"]["extra"], - threads: int(workflow.cores * 0.25) + threads: max(1, int(workflow.cores * 0.25)) wrapper: "v7.2.0/bio/star/align" @@ -72,7 +72,7 @@ rule samtools_sort: "--- Sort reads after mapping." params: extra=config["samtools"]["sort"], - threads: 2 + threads: max(1, int(workflow.cores * 0.25)) wrapper: "v7.0.0/bio/samtools/sort" @@ -88,6 +88,6 @@ rule samtools_index: "--- Index reads." params: extra=config["samtools"]["index"], - threads: 2 + threads: max(1, int(workflow.cores * 0.25)) wrapper: "v7.0.0/bio/samtools/index" diff --git a/workflow/rules/qc.smk b/workflow/rules/qc.smk index 54e940d..a362ec0 100644 --- a/workflow/rules/qc.smk +++ b/workflow/rules/qc.smk @@ -10,7 +10,7 @@ rule fastqc: "--- Checking fastq files with FastQC" log: "results/fastqc/{sample}.bwa.{read}.log", - threads: 2 + threads: max(1, int(workflow.cores * 0.25)) resources: mem_mb=4096, wrapper: @@ -26,7 +26,7 @@ rule alignment_stats: "results/qc/{step}/{sample}_flagstat.log", message: "--- Generate mapping statistics of BAM file using samtools." - threads: int(workflow.cores * 0.25) + threads: max(1, int(workflow.cores * 0.25)) wrapper: "v7.5.0/bio/samtools/flagstat" diff --git a/workflow/rules/quantification.smk b/workflow/rules/quantification.smk index 913f011..0551016 100644 --- a/workflow/rules/quantification.smk +++ b/workflow/rules/quantification.smk @@ -44,7 +44,7 @@ rule quantify_biotypes: "--- Quantify biotpyes with subread's featureCount." log: path="results/qc/biotypes/{sample}.counts.log", - threads: int(workflow.cores * 0.25) + threads: max(1, int(workflow.cores * 0.25)) params: defaults=config["feature_counts"]["defaults"], libtype=config["libtype"], diff --git a/workflow/rules/trim.smk b/workflow/rules/trim.smk index 5b53905..cdb451a 100644 --- a/workflow/rules/trim.smk +++ b/workflow/rules/trim.smk @@ -14,7 +14,7 @@ rule fastp: "trimming and QC filtering reads using fastp" params: extra=config["fastp"]["extra"], - threads: 2 + threads: max(1, int(workflow.cores * 0.25)) resources: mem_mb=4096, wrapper: From 80dcc2cb1d9e98e0077628f931da157d9862bc56 Mon Sep 17 00:00:00 2001 From: Rina Ahmed-Begrich Date: Thu, 5 Mar 2026 12:05:52 +0100 Subject: [PATCH 22/27] chore: update dag. include workflow link in report. --- .test/config/multiqc_config.yml | 1 + config/multiqc_config.yml | 1 + resources/images/dag.png | Bin 98207 -> 132156 bytes 3 files changed, 2 insertions(+) diff --git a/.test/config/multiqc_config.yml b/.test/config/multiqc_config.yml index 1ad40a8..5ce6410 100644 --- a/.test/config/multiqc_config.yml +++ b/.test/config/multiqc_config.yml @@ -1,4 +1,5 @@ report_header_info: + - Snakemake workflow: https://github.com/MPUSP/snakemake-bacterial-rnaseq-processing - Contact E-mail: "bioinformatics@mpusp.mpg.de" - Application Type: "Bacterial RNAseq" - Sequencing Platform: "Illumina" diff --git a/config/multiqc_config.yml b/config/multiqc_config.yml index 1ad40a8..5ce6410 100644 --- a/config/multiqc_config.yml +++ b/config/multiqc_config.yml @@ -1,4 +1,5 @@ report_header_info: + - Snakemake workflow: https://github.com/MPUSP/snakemake-bacterial-rnaseq-processing - Contact E-mail: "bioinformatics@mpusp.mpg.de" - Application Type: "Bacterial RNAseq" - Sequencing Platform: "Illumina" diff --git a/resources/images/dag.png b/resources/images/dag.png index 5ab8b23c9e7034a4fc4ad94156461233f4a3025a..e25bc2b888de5b7a1cd60fe5d7aaca0f43a80f2b 100644 GIT binary patch literal 132156 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 aR~fq_Gdo_7mOm!`udb}ElyThb?*9W4po_2o literal 98207 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�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 BO-=v+ From db5a3e9245fcc0041d01c081967a9125c1e45a81 Mon Sep 17 00:00:00 2001 From: Rina Ahmed-Begrich Date: Thu, 5 Mar 2026 12:08:32 +0100 Subject: [PATCH 23/27] fix: adjustr folder structure for logging. --- workflow/rules/coverage.smk | 2 +- workflow/rules/dedup.smk | 2 +- workflow/rules/mapping.smk | 4 ++-- workflow/rules/qc.smk | 6 +++--- workflow/rules/quantification.smk | 2 +- workflow/rules/trim.smk | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/workflow/rules/coverage.smk b/workflow/rules/coverage.smk index d0607db..4a042ec 100644 --- a/workflow/rules/coverage.smk +++ b/workflow/rules/coverage.smk @@ -18,7 +18,7 @@ rule deeptools_coverage: + f" --filterRNAstrand {('reverse' if(config['libtype']== 'sense' and wc.strand== 'plus') or(config['libtype']== 'antisense' and wc.strand== 'minus') else 'forward')}" ), log: - "results/{step}/{sample}_cpm_{strand}.log", + "results/{step}/log/{sample}_cpm_{strand}.log", message: "generate normalized coverage files using deeptools" wrapper: diff --git a/workflow/rules/dedup.smk b/workflow/rules/dedup.smk index 93953a2..5c53a84 100644 --- a/workflow/rules/dedup.smk +++ b/workflow/rules/dedup.smk @@ -8,7 +8,7 @@ rule get_fastq: message: "obtaining fastq files" log: - "results/get_fastq/{sample}_{read}.log", + "results/get_fastq/log/{sample}_{read}.log", shell: "ln -s {input} {output.fastq};" "echo 'made symbolic link from {input} to {output.fastq}' > {log}" diff --git a/workflow/rules/mapping.smk b/workflow/rules/mapping.smk index 6f1bd4d..e8bf772 100644 --- a/workflow/rules/mapping.smk +++ b/workflow/rules/mapping.smk @@ -67,7 +67,7 @@ rule samtools_sort: output: "results/mapped/{sample}.bam", log: - "results/mapped/{sample}.log", + "results/mapped/log/{sample}.log", message: "--- Sort reads after mapping." params: @@ -83,7 +83,7 @@ rule samtools_index: output: "results/mapped/{sample}.bam.bai", log: - "results/mapped/{sample}_index.log", + "results/mapped/log/{sample}_index.log", message: "--- Index reads." params: diff --git a/workflow/rules/qc.smk b/workflow/rules/qc.smk index a362ec0..1c6003b 100644 --- a/workflow/rules/qc.smk +++ b/workflow/rules/qc.smk @@ -9,7 +9,7 @@ rule fastqc: message: "--- Checking fastq files with FastQC" log: - "results/fastqc/{sample}.bwa.{read}.log", + "results/fastqc/log/{sample}_{read}.log", threads: max(1, int(workflow.cores * 0.25)) resources: mem_mb=4096, @@ -23,7 +23,7 @@ rule alignment_stats: output: "results/qc/{step}/{sample}.flagstat", log: - "results/qc/{step}/{sample}_flagstat.log", + "results/qc/{step}/log/{sample}_flagstat.log", message: "--- Generate mapping statistics of BAM file using samtools." threads: max(1, int(workflow.cores * 0.25)) @@ -96,6 +96,6 @@ rule multiqc: message: "--- Generating MultiQC report for seq data" log: - "results/multiqc/multiqc.log", + "results/multiqc/log/multiqc.log", wrapper: "v7.5.0/bio/multiqc" diff --git a/workflow/rules/quantification.smk b/workflow/rules/quantification.smk index 0551016..db1ee6a 100644 --- a/workflow/rules/quantification.smk +++ b/workflow/rules/quantification.smk @@ -43,7 +43,7 @@ rule quantify_biotypes: message: "--- Quantify biotpyes with subread's featureCount." log: - path="results/qc/biotypes/{sample}.counts.log", + path="results/qc/biotypes/log/{sample}.counts.log", threads: max(1, int(workflow.cores * 0.25)) params: defaults=config["feature_counts"]["defaults"], diff --git a/workflow/rules/trim.smk b/workflow/rules/trim.smk index cdb451a..8ce1ae7 100644 --- a/workflow/rules/trim.smk +++ b/workflow/rules/trim.smk @@ -9,7 +9,7 @@ rule fastp: read=["read1", "read2"] if is_paired_end() else ["read1"], ), log: - "results/fastp/{sample}.log", + "results/fastp/log/{sample}.log", message: "trimming and QC filtering reads using fastp" params: From 74ac88eab4bfd3ba75d8653aa050685c2d102be3 Mon Sep 17 00:00:00 2001 From: Rina Ahmed-Begrich Date: Thu, 5 Mar 2026 14:32:54 +0100 Subject: [PATCH 24/27] fix: software version for multiqc report. --- .test/config/multiqc_config.yml | 2 +- config/multiqc_config.yml | 2 +- resources/conda_envs/conda_envs.log | 68 ++++------------------------- workflow/envs/quantify_biotypes.yml | 4 +- 4 files changed, 12 insertions(+), 64 deletions(-) diff --git a/.test/config/multiqc_config.yml b/.test/config/multiqc_config.yml index 5ce6410..8fcc93c 100644 --- a/.test/config/multiqc_config.yml +++ b/.test/config/multiqc_config.yml @@ -16,7 +16,7 @@ custom_logo_url: "https://www.mpusp.mpg.de/" custom_logo_title: "Max Planck Unit for the Science of Pathogens" skip_versions_section: false -disable_version_detection: true +disable_version_detection: false versions_table_group_header: "Workflow Analysis Steps" skip_generalstats: true diff --git a/config/multiqc_config.yml b/config/multiqc_config.yml index 5ce6410..8fcc93c 100644 --- a/config/multiqc_config.yml +++ b/config/multiqc_config.yml @@ -16,7 +16,7 @@ custom_logo_url: "https://www.mpusp.mpg.de/" custom_logo_title: "Max Planck Unit for the Science of Pathogens" skip_versions_section: false -disable_version_detection: true +disable_version_detection: false versions_table_group_header: "Workflow Analysis Steps" skip_generalstats: true diff --git a/resources/conda_envs/conda_envs.log b/resources/conda_envs/conda_envs.log index ec4f6fd..1e8af23 100644 --- a/resources/conda_envs/conda_envs.log +++ b/resources/conda_envs/conda_envs.log @@ -1,21 +1,12 @@ -name: cutadapt -channels: - - conda-forge - - bioconda -dependencies: - - cutadapt=4.9 -name: fastqc -channels: - - conda-forge - - bioconda -dependencies: - - fastqc=0.12.1 -name: multiqc +name: umitools channels: - conda-forge - bioconda dependencies: - - multiqc=1.24.1 + - umi_tools=1.1.6 + - samtools=1.23 + - htslib=1.23 + - dnaio=1.2.2 name: extract_features channels: - conda-forge @@ -27,60 +18,17 @@ channels: - conda-forge - bioconda dependencies: - - python=3.12.7 - - pandas=2.2.3 - - pyyaml=6.0.2 - - snakemake=8.25.5 -name: deeptools -channels: - - conda-forge - - bioconda -dependencies: - - deeptools=3.5.5 + - pyyaml=6.0.3 name: feature_counts channels: - conda-forge - bioconda dependencies: - subread=2.0.6 -name: get_genome -channels: - - conda-forge - - bioconda -dependencies: - - unzip=6.0 - - ncbi-datasets-cli=16.27.2 - - bcbio-gff=0.7.1 - - samtools=1.21 - - htslib=1.21 -name: samtools -channels: - - conda-forge - - bioconda -dependencies: - - samtools=1.21 - - htslib=1.21 -name: star -channels: - - conda-forge - - bioconda -dependencies: - - star=2.7.11b - - samtools=1.21 - - htslib=1.21 -name: umitools -channels: - - conda-forge - - bioconda -dependencies: - - umi_tools=1.1.5 - - samtools=1.21 - - htslib=1.21 - - dnaio=1.2.2 name: quantify_biotypes channels: - conda-forge - bioconda dependencies: - - python=3.12.7 - - pandas=2.2.3 + - python=3.13.12 + - pandas=2.3.2 diff --git a/workflow/envs/quantify_biotypes.yml b/workflow/envs/quantify_biotypes.yml index 85afc13..7147559 100644 --- a/workflow/envs/quantify_biotypes.yml +++ b/workflow/envs/quantify_biotypes.yml @@ -3,5 +3,5 @@ channels: - conda-forge - bioconda dependencies: - - python=3.12.7 - - pandas=2.2.3 + - python=3.13.12 + - pandas=2.3.2 From 2605322ecf0a397e070bb713c3c69f65b71d7d5a Mon Sep 17 00:00:00 2001 From: Rina Ahmed-Begrich Date: Thu, 5 Mar 2026 15:47:51 +0100 Subject: [PATCH 25/27] fix: update protocol definition configs. --- .test/config/config.yml | 15 +++--- config/config.yml | 7 +-- config/samplesheet/samples.tsv | 3 ++ resources/protocols/rnaseq_mpusp_custom.yml | 57 ++++++++------------- resources/protocols/rnaseq_neb_umi.yml | 5 +- resources/protocols/rnaseq_nextflex.yml | 6 ++- 6 files changed, 43 insertions(+), 50 deletions(-) create mode 100644 config/samplesheet/samples.tsv diff --git a/.test/config/config.yml b/.test/config/config.yml index 6a0a7c9..4e7823a 100644 --- a/.test/config/config.yml +++ b/.test/config/config.yml @@ -1,5 +1,5 @@ -samplesheet: "config/samplesheet/samples_mpusp_custom.tsv" -libtype: "sense" +samplesheet: "config/samplesheet/samples_neb_umi.tsv" +libtype: "antisense" get_genome: database: "ncbi" @@ -18,20 +18,21 @@ extract_features: biotypes: ["protein_coding", "pseudogene", "ncRNA", "rRNA", "tRNA"] umi_extraction: - method: "string" - pattern: "--bc-pattern=NNNCCCNNNCCCNNNC" + method: "none" + pattern: "" umi_dedup: "--edit-distance-threshold=0" fastp: - extra: "-M 10 -l 18 --adapter_sequence TACACGACGCTCTTCCGATCT --adapter_sequence_r2 AGATCGGAAGAGCGTCGTGTA" + extra: "-M 10 -l 18 --adapter_sequence AGATCGGAAGAGCACACGTCTGAACTCCAGTCA --adapter_sequence_r2 AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT" star: index: "--genomeSAindexNbases 9" extra: [ - "--outFilterMultimapNmax 10 ", + "--outFilterMultimapNmax 10", "--outSAMmultNmax 1", - "--outMultimapperOrder Random ", + "--outMultimapperOrder Random", + "--alignIntronMax 1", ] samtools: diff --git a/config/config.yml b/config/config.yml index 4fdc0e4..e9f1091 100644 --- a/config/config.yml +++ b/config/config.yml @@ -18,7 +18,7 @@ extract_features: biotypes: ["protein_coding", "pseudogene", "ncRNA", "rRNA", "tRNA"] umi_extraction: - method: "" + method: "none" pattern: "" umi_dedup: "--edit-distance-threshold=0" @@ -29,9 +29,10 @@ star: index: "--genomeSAindexNbases 9" extra: [ - "--outFilterMultimapNmax 10 ", + "--outFilterMultimapNmax 10", "--outSAMmultNmax 1", - "--outMultimapperOrder Random ", + "--outMultimapperOrder Random", + "--alignIntronMax 1", ] samtools: diff --git a/config/samplesheet/samples.tsv b/config/samplesheet/samples.tsv new file mode 100644 index 0000000..de20d76 --- /dev/null +++ b/config/samplesheet/samples.tsv @@ -0,0 +1,3 @@ +sample condition replicate read1 read2 readumi +RNA-1 RNA 1 .test/data/rnaseq_neb_umi/RNA-1_R1.fastq.gz .test/data/rnaseq_neb_umi/RNA-1_R2.fastq.gz .test/data/rnaseq_neb_umi/RNA-1_UMI.fastq.gz +RNA-2 RNA 2 .test/data/rnaseq_neb_umi/RNA-2_R1.fastq.gz .test/data/rnaseq_neb_umi/RNA-2_R2.fastq.gz .test/data/rnaseq_neb_umi/RNA-2_UMI.fastq.gz diff --git a/resources/protocols/rnaseq_mpusp_custom.yml b/resources/protocols/rnaseq_mpusp_custom.yml index bce0678..365d0e2 100644 --- a/resources/protocols/rnaseq_mpusp_custom.yml +++ b/resources/protocols/rnaseq_mpusp_custom.yml @@ -11,7 +11,7 @@ get_genome: "RefSeq": "gene", "RefSeq": "pseudogene", "RefSeq": "CDS", - "Protein Homology": "CDS" + "Protein Homology": "CDS", ] extract_features: @@ -22,37 +22,24 @@ umi_extraction: pattern: "--bc-pattern=NNNCCCNNNCCCNNNC" umi_dedup: "--edit-distance-threshold=0" -cutadapt: - read1_adapter: "TACACGACGCTCTTCCGATCT" - read2_adapter: "AGATCGGAAGAGCGTCGTGTA" - default: - [ - "-q 10 ", - "-m 18 ", - "--overlap=3" - ] +fastp: + extra: "-M 10 -l 18 --adapter_sequence TACACGACGCTCTTCCGATCT --adapter_sequence_r2 AGATCGGAAGAGCGTCGTGTA" star: - index: Null - genomeSAindexNbases: 9 - multi: 10 - sam_multi: 1 - intron_max: 1 - default: + index: "--genomeSAindexNbases 9" + extra: [ - "--readFilesCommand zcat ", - "--outSAMstrandField None ", - "--outSAMattributes All ", - "--outSAMattrIHstart 0 ", - "--outFilterType Normal ", - "--outFilterMultimapScoreRange 1 ", - "-o STARmappings ", - "--outSAMtype BAM Unsorted ", - "--outStd BAM_Unsorted ", - "--outMultimapperOrder Random ", + "--outFilterMultimapNmax 10", + "--outSAMmultNmax 1", + "--outMultimapperOrder Random", + "--alignIntronMax 1", "--alignEndsType EndToEnd", ] +samtools: + sort: "" + index: "" + feature_counts: defaults: [ @@ -61,18 +48,16 @@ feature_counts: "-g locus_tag", "-M", "--fracOverlap 0.2", - "--largestOverlap" + "--largestOverlap", ] deeptools: - bin_size: 1 - normalize: "CPM" - defaults: "--exactScaling" - paired_end: "--extendReads" + genome_size: 2000000 + extra: "--binSize 1 --normalizeUsing CPM --exactScaling --extendReads" + +fastqc: + extra: "--quiet --nogroup" multiqc: - config: "config/multiqc_config.yml" - defaults: ["--dirs-depth 2 ", - "--exclude general_stats ", - "--force ", - "--dirs"] + config: "config/multiqc_config.yml" + extra: "--dirs" diff --git a/resources/protocols/rnaseq_neb_umi.yml b/resources/protocols/rnaseq_neb_umi.yml index 4f3ea9d..4e7823a 100644 --- a/resources/protocols/rnaseq_neb_umi.yml +++ b/resources/protocols/rnaseq_neb_umi.yml @@ -29,9 +29,10 @@ star: index: "--genomeSAindexNbases 9" extra: [ - "--outFilterMultimapNmax 10 ", + "--outFilterMultimapNmax 10", "--outSAMmultNmax 1", - "--outMultimapperOrder Random ", + "--outMultimapperOrder Random", + "--alignIntronMax 1", ] samtools: diff --git a/resources/protocols/rnaseq_nextflex.yml b/resources/protocols/rnaseq_nextflex.yml index 0f44010..01aeb82 100644 --- a/resources/protocols/rnaseq_nextflex.yml +++ b/resources/protocols/rnaseq_nextflex.yml @@ -29,9 +29,11 @@ star: index: "--genomeSAindexNbases 9" extra: [ - "--outFilterMultimapNmax 10 ", + "--outFilterMultimapNmax 10", "--outSAMmultNmax 1", - "--outMultimapperOrder Random ", + "--outMultimapperOrder Random", + "--alignIntronMax 1", + "--alignEndsType EndToEnd", ] samtools: From 1761d67129baac622e8324a46f302ec1a05ad133 Mon Sep 17 00:00:00 2001 From: Rina Ahmed-Begrich Date: Thu, 5 Mar 2026 16:07:23 +0100 Subject: [PATCH 26/27] fix: update GH actions. added apptainer functionality. --- .github/workflows/conventional-prs.yml | 10 +---- .github/workflows/deploy-apptainer.yml | 15 +++++++ .github/workflows/main.yml | 56 -------------------------- .github/workflows/release-please.yml | 12 +----- .github/workflows/snakemake-tests.yml | 10 +++++ .snakemake-workflow-catalog.yml | 2 +- README.md | 9 +++-- 7 files changed, 35 insertions(+), 79 deletions(-) create mode 100644 .github/workflows/deploy-apptainer.yml delete mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/snakemake-tests.yml diff --git a/.github/workflows/conventional-prs.yml b/.github/workflows/conventional-prs.yml index 82028b7..4485a29 100644 --- a/.github/workflows/conventional-prs.yml +++ b/.github/workflows/conventional-prs.yml @@ -1,4 +1,3 @@ -name: Lint PR on: pull_request_target: types: @@ -11,10 +10,5 @@ permissions: pull-requests: read jobs: - main: - name: Validate PR title - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + conventional-prs: + uses: MPUSP/mpusp-github-actions/.github/workflows/conventional-prs.yml@main diff --git a/.github/workflows/deploy-apptainer.yml b/.github/workflows/deploy-apptainer.yml new file mode 100644 index 0000000..648c054 --- /dev/null +++ b/.github/workflows/deploy-apptainer.yml @@ -0,0 +1,15 @@ +on: + workflow_run: + workflows: ["release-please"] + types: + - completed + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + deploy-apptainer: + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + uses: MPUSP/mpusp-github-actions/.github/workflows/deploy-apptainer.yml@main diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 2565579..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Tests - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - Formatting: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Formatting - uses: super-linter/super-linter@v7 - env: - VALIDATE_ALL_CODEBASE: false - DEFAULT_BRANCH: main - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VALIDATE_SNAKEMAKE_SNAKEFMT: true - VALIDATE_YAML_PRETTIER: true - - Linting: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Lint workflow - uses: snakemake/snakemake-github-action@v2 - with: - directory: .test - snakefile: workflow/Snakefile - args: "--lint" - - Testing: - runs-on: ubuntu-latest - needs: - - Linting - - Formatting - steps: - - uses: actions/checkout@v4 - - - name: Test workflow - uses: snakemake/snakemake-github-action@v2 - with: - directory: .test - snakefile: workflow/Snakefile - args: "--sdm conda --show-failed-logs --cores 4 --conda-cleanup-pkgs cache --all-temp" - - - name: Test report - uses: snakemake/snakemake-github-action@v2 - with: - directory: .test - snakefile: workflow/Snakefile - args: "--cores 4 --report report.zip" diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index e9ec84c..8d71c7b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -1,20 +1,12 @@ on: push: - branches: - - main + branches: [main] permissions: contents: write pull-requests: write issues: write -name: release-please - jobs: release-please: - runs-on: ubuntu-latest - steps: - - uses: googleapis/release-please-action@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - release-type: go # just keep a changelog, no version anywhere outside of git tags + uses: MPUSP/mpusp-github-actions/.github/workflows/release-please.yml@main diff --git a/.github/workflows/snakemake-tests.yml b/.github/workflows/snakemake-tests.yml new file mode 100644 index 0000000..aa193c8 --- /dev/null +++ b/.github/workflows/snakemake-tests.yml @@ -0,0 +1,10 @@ +on: + pull_request: + branches: [main] + +jobs: + snakemake-tests: + uses: MPUSP/mpusp-github-actions/.github/workflows/snakemake-tests.yml@main + with: + cores: 2 + dryrun: false diff --git a/.snakemake-workflow-catalog.yml b/.snakemake-workflow-catalog.yml index a4fe5ac..b83a590 100644 --- a/.snakemake-workflow-catalog.yml +++ b/.snakemake-workflow-catalog.yml @@ -7,5 +7,5 @@ usage: software-stack-deployment: conda: true # whether pipeline works with '--sdm conda' apptainer: false # whether pipeline works with '--sdm apptainer/singularity' - apptainer+conda: false # whether pipeline works with '--sdm conda apptainer/singularity' + apptainer+conda: true # whether pipeline works with '--sdm conda apptainer/singularity' report: true # whether creation of reports using 'snakemake --report report.zip' is supported \ No newline at end of file diff --git a/README.md b/README.md index ea236a6..7120f3e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Snakemake workflow: bacterial-rnaseq-processing -![Platform](https://img.shields.io/badge/platform-all-green) [![Snakemake](https://img.shields.io/badge/snakemake-≥8.0.0-brightgreen.svg)](https://snakemake.github.io) -[![Tests](https://github.com/MPUSP/snakemake-bacterial-rnaseq-processing/actions/workflows/main.yml/badge.svg)](https://github.com/MPUSP/snakemake-bacterial-rnaseq-processing/actions/workflows/main.yml) +[![GitHub actions](https://github.com/MPUSP/snakemake-bacterial-rnaseq-processing/actions/workflows/snakemake-tests.yml/badge.svg)](https://github.com/MPUSP/snakemake-bacterial-rnaseq-processing/workflows/snakemake-tests.yml) [![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) -[![workflow catalog](https://img.shields.io/badge/Snakemake%20workflow%20catalog-darkgreen)](https://snakemake.github.io/snakemake-workflow-catalog) +[![run with apptainer](https://img.shields.io/badge/run_with-apptainer-darkblue)](https://apptainer.org/docs/user/latest/) +[![workflow catalog](https://img.shields.io/badge/Snakemake%20workflow%20catalog-darkgreen)](https://snakemake.github.io/snakemake-workflow-catalog/docs/workflows/MPUSP/snakemake-bacterial-rnaseq-processing.html) --- @@ -69,7 +69,7 @@ To run the workflow with test files using **conda**: snakemake --cores 2 --sdm conda --directory .test ``` -To run the workflow with **apptainer** / **singularity** (not yet supported): +To run the workflow with **apptainer**: ```bash snakemake --cores 2 --sdm conda apptainer --directory .test @@ -80,6 +80,7 @@ snakemake --cores 2 --sdm conda apptainer --directory .test - Dr Rina Ahmed-Begrich - Affiliation: [Max-Planck-Unit for the Science of Pathogens](https://www.mpusp.mpg.de/) (MPUSP), Berlin, Germany - ORCID profile: https://orcid.org/0000-0002-0656-1795 + - github page: https://github.com/rabioinf - Dr. Michael Jahn - Affiliation: [Max-Planck-Unit for the Science of Pathogens](https://www.mpusp.mpg.de/) (MPUSP), Berlin, Germany - ORCID profile: https://orcid.org/0000-0002-3913-153X From ac8ab3ccf637224f6190ac2abac0ad5b80ab1233 Mon Sep 17 00:00:00 2001 From: jahn Date: Fri, 6 Mar 2026 08:18:02 +0100 Subject: [PATCH 27/27] fix: added names to CI workflows --- .github/workflows/conventional-prs.yml | 2 ++ .github/workflows/deploy-apptainer.yml | 4 +++- .github/workflows/release-please.yml | 2 ++ .github/workflows/snakemake-tests.yml | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/conventional-prs.yml b/.github/workflows/conventional-prs.yml index 4485a29..d0f5164 100644 --- a/.github/workflows/conventional-prs.yml +++ b/.github/workflows/conventional-prs.yml @@ -1,3 +1,5 @@ +name: Conventional PRs + on: pull_request_target: types: diff --git a/.github/workflows/deploy-apptainer.yml b/.github/workflows/deploy-apptainer.yml index 648c054..aa93cbc 100644 --- a/.github/workflows/deploy-apptainer.yml +++ b/.github/workflows/deploy-apptainer.yml @@ -1,6 +1,8 @@ +name: Deploy Apptainer + on: workflow_run: - workflows: ["release-please"] + workflows: ["Release Please"] types: - completed workflow_dispatch: diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 8d71c7b..b103aa0 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -1,3 +1,5 @@ +name: Release Please + on: push: branches: [main] diff --git a/.github/workflows/snakemake-tests.yml b/.github/workflows/snakemake-tests.yml index aa193c8..7e58b06 100644 --- a/.github/workflows/snakemake-tests.yml +++ b/.github/workflows/snakemake-tests.yml @@ -1,3 +1,5 @@ +name: Snakemake Tests + on: pull_request: branches: [main]