diff --git a/.editorconfig b/.editorconfig
index 9b990088..dd9ffa53 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -18,7 +18,12 @@ end_of_line = unset
insert_final_newline = unset
trim_trailing_whitespace = unset
indent_style = unset
-indent_size = unset
+[/subworkflows/nf-core/**]
+charset = unset
+end_of_line = unset
+insert_final_newline = unset
+trim_trailing_whitespace = unset
+indent_style = unset
[/assets/email*]
indent_size = unset
@@ -28,5 +33,5 @@ indent_size = unset
indent_style = unset
# ignore python
-[*.{py}]
+[*.{py,md}]
indent_style = unset
diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml
index dc45f92c..ccd63262 100644
--- a/.github/workflows/awsfulltest.yml
+++ b/.github/workflows/awsfulltest.yml
@@ -28,7 +28,7 @@ jobs:
}
profiles: test_full
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4
with:
name: Tower debug log file
path: |
diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml
index ab4fd41a..d9028ab6 100644
--- a/.github/workflows/awstest.yml
+++ b/.github/workflows/awstest.yml
@@ -25,7 +25,7 @@ jobs:
}
profiles: test
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4
with:
name: Tower debug log file
path: |
diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml
index 2e9972f5..2dc0f5c7 100644
--- a/.github/workflows/branch.yml
+++ b/.github/workflows/branch.yml
@@ -19,7 +19,7 @@ jobs:
# NOTE - this doesn't currently work if the PR is coming from a fork, due to limitations in GitHub actions secrets
- name: Post PR comment
if: failure()
- uses: mshick/add-pr-comment@v2
+ uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2
with:
message: |
## This PR is against the `master` branch :x:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0c53df44..d5cf84c2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,13 +28,16 @@ jobs:
- "latest-everything"
steps:
- name: Check out pipeline code
- uses: actions/checkout@v4
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Install Nextflow
- uses: nf-core/setup-nextflow@v1
+ uses: nf-core/setup-nextflow@b9f764e8ba5c76b712ace14ecbfcef0e40ae2dd8 # v1
with:
version: "${{ matrix.NXF_VER }}"
+ - name: Disk space cleanup
+ uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
+
- name: Run pipeline with test data
run: |
nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results
diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml
index e37cfda5..0b6b1f27 100644
--- a/.github/workflows/clean-up.yml
+++ b/.github/workflows/clean-up.yml
@@ -10,7 +10,7 @@ jobs:
issues: write
pull-requests: write
steps:
- - uses: actions/stale@v9
+ - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9
with:
stale-issue-message: "This issue has been tagged as awaiting-changes or awaiting-feedback by an nf-core contributor. Remove stale label or add a comment otherwise this issue will be closed in 20 days."
stale-pr-message: "This PR has been tagged as awaiting-changes or awaiting-feedback by an nf-core contributor. Remove stale label or add a comment if it is still useful."
diff --git a/.github/workflows/download_pipeline.yml b/.github/workflows/download_pipeline.yml
index 8a330045..f823210d 100644
--- a/.github/workflows/download_pipeline.yml
+++ b/.github/workflows/download_pipeline.yml
@@ -6,6 +6,11 @@ name: Test successful pipeline download with 'nf-core download'
# - the head branch of the pull request is updated, i.e. if fixes for a release are pushed last minute to dev.
on:
workflow_dispatch:
+ inputs:
+ testbranch:
+ description: "The specific branch you wish to utilize for the test execution of nf-core download."
+ required: true
+ default: "dev"
pull_request:
types:
- opened
@@ -23,13 +28,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install Nextflow
- uses: nf-core/setup-nextflow@v1
+ uses: nf-core/setup-nextflow@b9f764e8ba5c76b712ace14ecbfcef0e40ae2dd8 # v1
- - uses: actions/setup-python@v5
+ - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5
with:
python-version: "3.11"
architecture: "x64"
- - uses: eWaterCycle/setup-singularity@v7
+ - uses: eWaterCycle/setup-singularity@931d4e31109e875b13309ae1d07c70ca8fbc8537 # v7
with:
singularity-version: 3.8.3
@@ -42,13 +47,13 @@ jobs:
run: |
echo "REPO_LOWERCASE=${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV}
echo "REPOTITLE_LOWERCASE=$(basename ${GITHUB_REPOSITORY,,})" >> ${GITHUB_ENV}
- echo "REPO_BRANCH=${GITHUB_REF#refs/heads/}" >> ${GITHUB_ENV}
+ echo "REPO_BRANCH=${{ github.event.inputs.testbranch || 'dev' }}" >> ${GITHUB_ENV}
- name: Download the pipeline
env:
NXF_SINGULARITY_CACHEDIR: ./
run: |
- nf-core download ${{ env.REPO_LOWERCASE }} \
+ nf-core download ${{ env.REPO_LOWERCASE }} \
--revision ${{ env.REPO_BRANCH }} \
--outdir ./${{ env.REPOTITLE_LOWERCASE }} \
--compress "none" \
diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
index 81cd098e..748b4311 100644
--- a/.github/workflows/linting.yml
+++ b/.github/workflows/linting.yml
@@ -14,10 +14,10 @@ jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Set up Python 3.11
- uses: actions/setup-python@v5
+ uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5
with:
python-version: 3.11
cache: "pip"
@@ -32,12 +32,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out pipeline code
- uses: actions/checkout@v4
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Install Nextflow
- uses: nf-core/setup-nextflow@v1
+ uses: nf-core/setup-nextflow@b9f764e8ba5c76b712ace14ecbfcef0e40ae2dd8 # v1
- - uses: actions/setup-python@v5
+ - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5
with:
python-version: "3.11"
architecture: "x64"
@@ -60,7 +60,7 @@ jobs:
- name: Upload linting log file artifact
if: ${{ always() }}
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4
with:
name: linting-logs
path: |
diff --git a/.github/workflows/linting_comment.yml b/.github/workflows/linting_comment.yml
index 147bcd10..b706875f 100644
--- a/.github/workflows/linting_comment.yml
+++ b/.github/workflows/linting_comment.yml
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download lint results
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@f6b0bace624032e30a85a8fd9c1a7f8f611f5737 # v3
with:
workflow: linting.yml
workflow_conclusion: completed
@@ -21,7 +21,7 @@ jobs:
run: echo "pr_number=$(cat linting-logs/PR_number.txt)" >> $GITHUB_OUTPUT
- name: Post PR comment
- uses: marocchino/sticky-pull-request-comment@v2
+ uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
number: ${{ steps.pr_number.outputs.pr_number }}
diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml
index 21ac3f06..c3674af2 100644
--- a/.github/workflows/release-announcements.yml
+++ b/.github/workflows/release-announcements.yml
@@ -9,6 +9,11 @@ jobs:
toot:
runs-on: ubuntu-latest
steps:
+ - name: get topics and convert to hashtags
+ id: get_topics
+ run: |
+ curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.name == "${{ github.repository }}") | .topics[]' | awk '{print "#"$0}' | tr '\n' ' ' > $GITHUB_OUTPUT
+
- uses: rzr/fediverse-action@master
with:
access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }}
@@ -20,11 +25,13 @@ jobs:
Please see the changelog: ${{ github.event.release.html_url }}
+ ${{ steps.get_topics.outputs.GITHUB_OUTPUT }} #nfcore #openscience #nextflow #bioinformatics
+
send-tweet:
runs-on: ubuntu-latest
steps:
- - uses: actions/setup-python@v5
+ - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5
with:
python-version: "3.10"
- name: Install dependencies
@@ -56,7 +63,7 @@ jobs:
bsky-post:
runs-on: ubuntu-latest
steps:
- - uses: zentered/bluesky-post-action@v0.1.0
+ - uses: zentered/bluesky-post-action@80dbe0a7697de18c15ad22f4619919ceb5ccf597 # v0.1.0
with:
post: |
Pipeline release! ${{ github.repository }} v${{ github.event.release.tag_name }} - ${{ github.event.release.name }}!
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 286aa207..a4437788 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,15 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### `Added`
- Added MS²Rescore module with the underlying python CLI [#293](https://github.com/nf-core/mhcquant/pull/293)
-- Template update 2.11 [#300](https://github.com/nf-core/mhcquant/pull/300)
-- Template update 2.12 [#303](https://github.com/nf-core/mhcquant/pull/303)
### `Fixed`
- Create only one decoy database [#299](https://github.com/nf-core/mhcquant/pull/299)
+- Template update 2.11 [#300](https://github.com/nf-core/mhcquant/pull/300)
+- Template update 2.12 [#303](https://github.com/nf-core/mhcquant/pull/303)
- Use `groupKey` to streamline group-wise processing [#310](https://github.com/nf-core/mhcquant/pull/310)
- Replace `PYOPENMS_IDFILTER` with `OPENMS_IDFILTER` [#310](https://github.com/nf-core/mhcquant/pull/310)
- Added nf-core modules [#310](https://github.com/nf-core/mhcquant/pull/310)
+- Template update 2.13 [#311](https://github.com/nf-core/mhcquant/pull/311)
### `Deprecated`
diff --git a/README.md b/README.md
index 10158f28..4f3e07d8 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,9 @@
-[![GitHub Actions CI Status](https://github.com/nf-core/mhcquant/workflows/nf-core%20CI/badge.svg)](https://github.com/nf-core/mhcquant/actions?query=workflow%3A%22nf-core+CI%22)
-[![GitHub Actions Linting Status](https://github.com/nf-core/mhcquant/workflows/nf-core%20linting/badge.svg)](https://github.com/nf-core/mhcquant/actions?query=workflow%3A%22nf-core+linting%22)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/mhcquant/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.8427707-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.8427707)
+
+[![GitHub Actions CI Status](https://github.com/nf-core/mhcquant/actions/workflows/ci.yml/badge.svg)](https://github.com/nf-core/mhcquant/actions/workflows/ci.yml)
+[![GitHub Actions Linting Status](https://github.com/nf-core/mhcquant/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/mhcquant/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/mhcquant/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.8427707-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.8427707)
[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A523.04.0-23aa62.svg)](https://www.nextflow.io/)
[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)
diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml
index a4906753..5d8e4954 100644
--- a/assets/multiqc_config.yml
+++ b/assets/multiqc_config.yml
@@ -15,3 +15,5 @@ report_section_order:
order: -1002
export_plots: true
+
+disable_version_detection: true
diff --git a/assets/schema_input.json b/assets/schema_input.json
index 6df68dfd..da298c60 100644
--- a/assets/schema_input.json
+++ b/assets/schema_input.json
@@ -9,35 +9,27 @@
"properties": {
"ID": {
"type": "integer",
- "errorMessage": "Provide an unique identifier for the replicate, must be a numeric value"
+ "errorMessage": "Provide an unique identifier for the replicate, must be a numeric value",
+ "meta": ["id"]
},
"Sample": {
"type": "string",
- "pattern": "^\\S+-?",
- "errorMessage": "Sample name must be provided and cannot contain spaces"
+ "pattern": "^\\S+$",
+ "errorMessage": "Sample name must be provided and cannot contain spaces",
+ "meta": ["sample"]
},
"Condition": {
"type": "string",
"pattern": "^\\S+-?",
- "errorMessage": "Sample condition must be provided and cannot contain spaces"
+ "errorMessage": "Sample condition must be provided and cannot contain spaces",
+ "meta": ["condition"]
},
"ReplicateFileName": {
"type": "string",
- "errorMessage": "MS file spaces and must have extension '.raw'|'.RAW', '.mzml'|'mzML', or '.d'",
- "anyOf": [
- {
- "type": "string",
- "pattern": "^(?i)\\S+-?\\.raw$"
- },
- {
- "type": "string",
- "pattern": "^(?i)\\S+-?\\.mzML$"
- },
- {
- "type": "string",
- "pattern": "^\\S+-?\\.d$"
- }
- ]
+ "format": "file-path",
+ "exists": true,
+ "pattern": "^\\S+-?\\.(raw|RAW|mzML|mzML.gz|d|d.tar|d.tar.gz|d.zip)$",
+ "errorMessage": "MS file cannot contain spaces and must have one of the extensions: raw | RAW | mzML | mzML.gz | d | d.tar | d.tar.gz | d.zip"
}
},
"required": ["ID", "Sample", "Condition", "ReplicateFileName"]
diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py
deleted file mode 100755
index 9154eb41..00000000
--- a/bin/check_samplesheet.py
+++ /dev/null
@@ -1,241 +0,0 @@
-#!/usr/bin/env python
-
-# Written by Marissa Dubbelaar and released under MIT license.
-
-"""Provide a command line tool to validate and transform tabular samplesheets."""
-
-import os
-import argparse
-import csv
-import logging
-import sys
-from collections import Counter
-from pathlib import Path
-
-logger = logging.getLogger()
-
-
-class RowChecker:
- """
- Define a service that can validate and transform each given row.
-
- Attributes:
- modified (list): A list of dicts, where each dict corresponds to a previously
- validated and transformed row. The order of rows is maintained.
-
- """
-
- VALID_FORMATS = (".raw", ".mzml", ".d")
-
- def __init__(
- self,
- id_col="ID",
- sample_col="Sample",
- condition_col="Condition",
- filename_col="ReplicateFileName",
- **kwargs,
- ):
- """
- Initialize the row checker with the expected column names.
- Args:
- id_col (int) : An integer that acts as a unique identifier for the
- unique samples.
- sample_col (str): The name of the column that contains the sample name
- (default "sample").
- condition_col (str): This column consists of additional information about
- the sample, could be anything from a "wildtype/disease" description
- to a particular treatment (default "").
- filename_col (str): The name of the column that contains the path to the
- raw or mzMl files ).
- """
- super().__init__(**kwargs)
- self._id_col = id_col
- self._sample_col = sample_col
- self._condition_col = condition_col
- self._filename_col = filename_col
- self._seen = set()
- self.modified = []
-
- def validate_and_transform(self, row):
- """
- Perform all validations on the given row and insert the read pairing status.
- Args:
- row (dict): A mapping from column headers (keys) to elements of that row
- (values).
- """
- self._validate_id(row)
- self._validate_sample(row)
- self._validate_condition(row)
- self._validate_filename(row)
- self._seen.add((row[self._sample_col], row[self._filename_col]))
- self.modified.append(row)
-
- def _validate_id(self, row):
- """Assert that the sample name exists and convert spaces to underscores."""
- assert row[self._id_col].isdigit(), "Make sure that the sample ID is an numeric value"
-
- def _validate_sample(self, row):
- """Assert that the sample name exists and convert spaces to underscores."""
- assert len(row[self._sample_col]) > 0, "Sample input is required."
- # Sanitize samples slightly.
- row[self._sample_col] = row[self._sample_col].replace(" ", "_")
-
- def _validate_condition(self, row):
- """Assert that the condition is provided and convert spaces to underscores."""
- assert len(row[self._condition_col]) > 0, "Sample input is required."
- # Sanitize samples slightly.
- row[self._condition_col] = row[self._condition_col].replace(" ", "_")
-
- def _validate_filename(self, row):
- """Assert that the data entry has the right format if it exists."""
- if len(row[self._filename_col]) > 0:
- self._validate_ms_format(row[self._filename_col])
-
- def _validate_ms_format(self, filename):
- """Assert that a given filename has one of the expected MS extensions."""
- assert any(filename.lower().endswith(extension) for extension in self.VALID_FORMATS), (
- f"The file has an unrecognized extension: {filename}\n"
- f"It should be one of: {', '.join(self.VALID_FORMATS)}"
- )
-
- def validate_unique_samples(self):
- """
- Assert that the combination of sample name and filename is unique.
- In addition to the validation, also rename the sample if more than one sample
- file combination exists.
- """
- assert len(self._seen) == len(self.modified), "The pair of sample name and file must be unique."
- if len({pair[0] for pair in self._seen}) < len(self._seen):
- counts = Counter(pair[0] for pair in self._seen)
- seen = Counter()
- for row in self.modified:
- sample = row[self._sample_col]
- seen[sample] += 1
- if counts[sample] > 1:
- row[self._sample_col] = f"{sample}"
-
-
-def read_head(handle, num_lines=10):
- """Read the specified number of lines from the current position in the file."""
- lines = []
- for idx, line in enumerate(handle):
- if idx == num_lines:
- break
- lines.append(line)
- return "".join(lines)
-
-
-def sniff_format(handle):
- """
- Detect the tabular format.
-
- Args:
- handle (text file): A handle to a `text file`_ object. The read position is
- expected to be at the beginning (index 0).
-
- Returns:
- csv.Dialect: The detected tabular format.
-
- .. _text file:
- https://docs.python.org/3/glossary.html#term-text-file
-
- """
- peek = read_head(handle)
- handle.seek(0)
- sniffer = csv.Sniffer()
- dialect = sniffer.sniff(peek)
- return dialect
-
-
-def check_samplesheet(file_in, file_out):
- """
- Check that the tabular samplesheet has the structure expected by nf-core pipelines.
- Validate the general shape of the table, expected columns, and each row. Also add
- an additional column which records whether one or two FASTQ reads were found.
- Args:
- file_in (pathlib.Path): The given tabular samplesheet. The format can be either
- CSV, TSV, or any other format automatically recognized by ``csv.Sniffer``.
- file_out (pathlib.Path): Where the validated and transformed samplesheet should
- be created; always in CSV format.
- Example:
- This function checks that the samplesheet follows the following structure,
- see also the `viral recon samplesheet`_::
- ID Sample Condition ReplicateFileName
- 1 WT A WT_A.raw
- 2 WT B WT_B.raw
- 3 KO A KO_A.raw
- 4 KO B KO_B.raw
- .. _viral recon samplesheet:
- https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv
- """
- required_columns = {"ID", "Sample", "Condition", "ReplicateFileName"}
-
- # See https://docs.python.org/3.9/library/csv.html#id3 to read up on `newline=""`.
- with file_in.open(newline="") as in_handle:
- reader = csv.DictReader(in_handle, dialect=sniff_format(in_handle))
- # Validate the existence of the expected header columns.
- if not required_columns.issubset(reader.fieldnames):
- req_cols = ", ".join(required_columns)
- logger.critical(f"The sample sheet **must** contain these column headers: {req_cols}.")
- sys.exit(1)
- # Validate each row.
- checker = RowChecker()
- for i, row in enumerate(reader):
- try:
- checker.validate_and_transform(row)
- except AssertionError as error:
- logger.critical(f"{str(error)} On line {i + 2}.")
- sys.exit(1)
- checker.validate_unique_samples()
- header = list(reader.fieldnames)
- header.insert(4, "Extension")
- # See https://docs.python.org/3.9/library/csv.html#id3 to read up on `newline=""`.
- with file_out.open(mode="w", newline="") as out_handle:
- writer = csv.DictWriter(out_handle, header, delimiter="\t")
- writer.writeheader()
- for row in checker.modified:
- row["Extension"] = os.path.splitext(row["ReplicateFileName"])[1][1:].lower()
- writer.writerow(row)
-
-
-def parse_args(argv=None):
- """Define and immediately parse command line arguments."""
- parser = argparse.ArgumentParser(
- description="Validate and transform a tabular samplesheet.",
- epilog="Example: python check_samplesheet.py samplesheet.csv samplesheet.valid.csv",
- )
- parser.add_argument(
- "file_in",
- metavar="FILE_IN",
- type=Path,
- help="Tabular input samplesheet in CSV or TSV format.",
- )
- parser.add_argument(
- "file_out",
- metavar="FILE_OUT",
- type=Path,
- help="Transformed output samplesheet in CSV format.",
- )
- parser.add_argument(
- "-l",
- "--log-level",
- help="The desired log level (default WARNING).",
- choices=("CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"),
- default="WARNING",
- )
- return parser.parse_args(argv)
-
-
-def main(argv=None):
- """Coordinate argument parsing and program execution."""
- args = parse_args(argv)
- logging.basicConfig(level=args.log_level, format="[%(levelname)s] %(message)s")
- if not args.file_in.is_file():
- logger.error(f"The given input file {args.file_in} was not found!")
- sys.exit(2)
- args.file_out.parent.mkdir(parents=True, exist_ok=True)
- check_samplesheet(args.file_in, args.file_out)
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy
deleted file mode 100755
index e248e4c3..00000000
--- a/lib/NfcoreTemplate.groovy
+++ /dev/null
@@ -1,356 +0,0 @@
-//
-// This file holds several functions used within the nf-core pipeline template.
-//
-
-import org.yaml.snakeyaml.Yaml
-import groovy.json.JsonOutput
-import nextflow.extension.FilesEx
-
-class NfcoreTemplate {
-
- //
- // Check AWS Batch related parameters have been specified correctly
- //
- public static void awsBatch(workflow, params) {
- if (workflow.profile.contains('awsbatch')) {
- // Check params.awsqueue and params.awsregion have been set if running on AWSBatch
- assert (params.awsqueue && params.awsregion) : "Specify correct --awsqueue and --awsregion parameters on AWSBatch!"
- // Check outdir paths to be S3 buckets if running on AWSBatch
- assert params.outdir.startsWith('s3:') : "Outdir not on S3 - specify S3 Bucket to run on AWSBatch!"
- }
- }
-
- //
- // Warn if a -profile or Nextflow config has not been provided to run the pipeline
- //
- public static void checkConfigProvided(workflow, log) {
- if (workflow.profile == 'standard' && workflow.configFiles.size() <= 1) {
- log.warn "[$workflow.manifest.name] You are attempting to run the pipeline without any custom configuration!\n\n" +
- "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" +
- " (1) Using an existing pipeline profile e.g. `-profile docker` or `-profile singularity`\n" +
- " (2) Using an existing nf-core/configs for your Institution e.g. `-profile crick` or `-profile uppmax`\n" +
- " (3) Using your own local custom config e.g. `-c /path/to/your/custom.config`\n\n" +
- "Please refer to the quick start section and usage docs for the pipeline.\n "
- }
- }
-
- //
- // Generate version string
- //
- public static String version(workflow) {
- String version_string = ""
-
- if (workflow.manifest.version) {
- def prefix_v = workflow.manifest.version[0] != 'v' ? 'v' : ''
- version_string += "${prefix_v}${workflow.manifest.version}"
- }
-
- if (workflow.commitId) {
- def git_shortsha = workflow.commitId.substring(0, 7)
- version_string += "-g${git_shortsha}"
- }
-
- return version_string
- }
-
- //
- // Construct and send completion email
- //
- public static void email(workflow, params, summary_params, projectDir, log, multiqc_report=[]) {
-
- // Set up the e-mail variables
- def subject = "[$workflow.manifest.name] Successful: $workflow.runName"
- if (!workflow.success) {
- subject = "[$workflow.manifest.name] FAILED: $workflow.runName"
- }
-
- def summary = [:]
- for (group in summary_params.keySet()) {
- summary << summary_params[group]
- }
-
- def misc_fields = [:]
- misc_fields['Date Started'] = workflow.start
- misc_fields['Date Completed'] = workflow.complete
- misc_fields['Pipeline script file path'] = workflow.scriptFile
- misc_fields['Pipeline script hash ID'] = workflow.scriptId
- if (workflow.repository) misc_fields['Pipeline repository Git URL'] = workflow.repository
- if (workflow.commitId) misc_fields['Pipeline repository Git Commit'] = workflow.commitId
- if (workflow.revision) misc_fields['Pipeline Git branch/tag'] = workflow.revision
- misc_fields['Nextflow Version'] = workflow.nextflow.version
- misc_fields['Nextflow Build'] = workflow.nextflow.build
- misc_fields['Nextflow Compile Timestamp'] = workflow.nextflow.timestamp
-
- def email_fields = [:]
- email_fields['version'] = NfcoreTemplate.version(workflow)
- email_fields['runName'] = workflow.runName
- email_fields['success'] = workflow.success
- email_fields['dateComplete'] = workflow.complete
- email_fields['duration'] = workflow.duration
- email_fields['exitStatus'] = workflow.exitStatus
- email_fields['errorMessage'] = (workflow.errorMessage ?: 'None')
- email_fields['errorReport'] = (workflow.errorReport ?: 'None')
- email_fields['commandLine'] = workflow.commandLine
- email_fields['projectDir'] = workflow.projectDir
- email_fields['summary'] = summary << misc_fields
-
- // On success try attach the multiqc report
- def mqc_report = null
- try {
- if (workflow.success) {
- mqc_report = multiqc_report.getVal()
- if (mqc_report.getClass() == ArrayList && mqc_report.size() >= 1) {
- if (mqc_report.size() > 1) {
- log.warn "[$workflow.manifest.name] Found multiple reports from process 'MULTIQC', will use only one"
- }
- mqc_report = mqc_report[0]
- }
- }
- } catch (all) {
- if (multiqc_report) {
- log.warn "[$workflow.manifest.name] Could not attach MultiQC report to summary email"
- }
- }
-
- // Check if we are only sending emails on failure
- def email_address = params.email
- if (!params.email && params.email_on_fail && !workflow.success) {
- email_address = params.email_on_fail
- }
-
- // Render the TXT template
- def engine = new groovy.text.GStringTemplateEngine()
- def tf = new File("$projectDir/assets/email_template.txt")
- def txt_template = engine.createTemplate(tf).make(email_fields)
- def email_txt = txt_template.toString()
-
- // Render the HTML template
- def hf = new File("$projectDir/assets/email_template.html")
- def html_template = engine.createTemplate(hf).make(email_fields)
- def email_html = html_template.toString()
-
- // Render the sendmail template
- def max_multiqc_email_size = (params.containsKey('max_multiqc_email_size') ? params.max_multiqc_email_size : 0) as nextflow.util.MemoryUnit
- def smail_fields = [ email: email_address, subject: subject, email_txt: email_txt, email_html: email_html, projectDir: "$projectDir", mqcFile: mqc_report, mqcMaxSize: max_multiqc_email_size.toBytes() ]
- def sf = new File("$projectDir/assets/sendmail_template.txt")
- def sendmail_template = engine.createTemplate(sf).make(smail_fields)
- def sendmail_html = sendmail_template.toString()
-
- // Send the HTML e-mail
- Map colors = logColours(params.monochrome_logs)
- if (email_address) {
- try {
- if (params.plaintext_email) { throw GroovyException('Send plaintext e-mail, not HTML') }
- // Try to send HTML e-mail using sendmail
- def sendmail_tf = new File(workflow.launchDir.toString(), ".sendmail_tmp.html")
- sendmail_tf.withWriter { w -> w << sendmail_html }
- [ 'sendmail', '-t' ].execute() << sendmail_html
- log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (sendmail)-"
- } catch (all) {
- // Catch failures and try with plaintext
- def mail_cmd = [ 'mail', '-s', subject, '--content-type=text/html', email_address ]
- if ( mqc_report != null && mqc_report.size() <= max_multiqc_email_size.toBytes() ) {
- mail_cmd += [ '-A', mqc_report ]
- }
- mail_cmd.execute() << email_html
- log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (mail)-"
- }
- }
-
- // Write summary e-mail HTML to a file
- def output_hf = new File(workflow.launchDir.toString(), ".pipeline_report.html")
- output_hf.withWriter { w -> w << email_html }
- FilesEx.copyTo(output_hf.toPath(), "${params.outdir}/pipeline_info/pipeline_report.html");
- output_hf.delete()
-
- // Write summary e-mail TXT to a file
- def output_tf = new File(workflow.launchDir.toString(), ".pipeline_report.txt")
- output_tf.withWriter { w -> w << email_txt }
- FilesEx.copyTo(output_tf.toPath(), "${params.outdir}/pipeline_info/pipeline_report.txt");
- output_tf.delete()
- }
-
- //
- // Construct and send a notification to a web server as JSON
- // e.g. Microsoft Teams and Slack
- //
- public static void IM_notification(workflow, params, summary_params, projectDir, log) {
- def hook_url = params.hook_url
-
- def summary = [:]
- for (group in summary_params.keySet()) {
- summary << summary_params[group]
- }
-
- def misc_fields = [:]
- misc_fields['start'] = workflow.start
- misc_fields['complete'] = workflow.complete
- misc_fields['scriptfile'] = workflow.scriptFile
- misc_fields['scriptid'] = workflow.scriptId
- if (workflow.repository) misc_fields['repository'] = workflow.repository
- if (workflow.commitId) misc_fields['commitid'] = workflow.commitId
- if (workflow.revision) misc_fields['revision'] = workflow.revision
- misc_fields['nxf_version'] = workflow.nextflow.version
- misc_fields['nxf_build'] = workflow.nextflow.build
- misc_fields['nxf_timestamp'] = workflow.nextflow.timestamp
-
- def msg_fields = [:]
- msg_fields['version'] = NfcoreTemplate.version(workflow)
- msg_fields['runName'] = workflow.runName
- msg_fields['success'] = workflow.success
- msg_fields['dateComplete'] = workflow.complete
- msg_fields['duration'] = workflow.duration
- msg_fields['exitStatus'] = workflow.exitStatus
- msg_fields['errorMessage'] = (workflow.errorMessage ?: 'None')
- msg_fields['errorReport'] = (workflow.errorReport ?: 'None')
- msg_fields['commandLine'] = workflow.commandLine.replaceFirst(/ +--hook_url +[^ ]+/, "")
- msg_fields['projectDir'] = workflow.projectDir
- msg_fields['summary'] = summary << misc_fields
-
- // Render the JSON template
- def engine = new groovy.text.GStringTemplateEngine()
- // Different JSON depending on the service provider
- // Defaults to "Adaptive Cards" (https://adaptivecards.io), except Slack which has its own format
- def json_path = hook_url.contains("hooks.slack.com") ? "slackreport.json" : "adaptivecard.json"
- def hf = new File("$projectDir/assets/${json_path}")
- def json_template = engine.createTemplate(hf).make(msg_fields)
- def json_message = json_template.toString()
-
- // POST
- def post = new URL(hook_url).openConnection();
- post.setRequestMethod("POST")
- post.setDoOutput(true)
- post.setRequestProperty("Content-Type", "application/json")
- post.getOutputStream().write(json_message.getBytes("UTF-8"));
- def postRC = post.getResponseCode();
- if (! postRC.equals(200)) {
- log.warn(post.getErrorStream().getText());
- }
- }
-
- //
- // Dump pipeline parameters in a json file
- //
- public static void dump_parameters(workflow, params) {
- def timestamp = new java.util.Date().format( 'yyyy-MM-dd_HH-mm-ss')
- def filename = "params_${timestamp}.json"
- def temp_pf = new File(workflow.launchDir.toString(), ".${filename}")
- def jsonStr = JsonOutput.toJson(params)
- temp_pf.text = JsonOutput.prettyPrint(jsonStr)
-
- FilesEx.copyTo(temp_pf.toPath(), "${params.outdir}/pipeline_info/params_${timestamp}.json")
- temp_pf.delete()
- }
-
- //
- // Print pipeline summary on completion
- //
- public static void summary(workflow, params, log) {
- Map colors = logColours(params.monochrome_logs)
- if (workflow.success) {
- if (workflow.stats.ignoredCount == 0) {
- log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Pipeline completed successfully${colors.reset}-"
- } else {
- log.info "-${colors.purple}[$workflow.manifest.name]${colors.yellow} Pipeline completed successfully, but with errored process(es) ${colors.reset}-"
- }
- } else {
- log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-"
- }
- }
-
- //
- // ANSII Colours used for terminal logging
- //
- public static Map logColours(Boolean monochrome_logs) {
- Map colorcodes = [:]
-
- // Reset / Meta
- colorcodes['reset'] = monochrome_logs ? '' : "\033[0m"
- colorcodes['bold'] = monochrome_logs ? '' : "\033[1m"
- colorcodes['dim'] = monochrome_logs ? '' : "\033[2m"
- colorcodes['underlined'] = monochrome_logs ? '' : "\033[4m"
- colorcodes['blink'] = monochrome_logs ? '' : "\033[5m"
- colorcodes['reverse'] = monochrome_logs ? '' : "\033[7m"
- colorcodes['hidden'] = monochrome_logs ? '' : "\033[8m"
-
- // Regular Colors
- colorcodes['black'] = monochrome_logs ? '' : "\033[0;30m"
- colorcodes['red'] = monochrome_logs ? '' : "\033[0;31m"
- colorcodes['green'] = monochrome_logs ? '' : "\033[0;32m"
- colorcodes['yellow'] = monochrome_logs ? '' : "\033[0;33m"
- colorcodes['blue'] = monochrome_logs ? '' : "\033[0;34m"
- colorcodes['purple'] = monochrome_logs ? '' : "\033[0;35m"
- colorcodes['cyan'] = monochrome_logs ? '' : "\033[0;36m"
- colorcodes['white'] = monochrome_logs ? '' : "\033[0;37m"
-
- // Bold
- colorcodes['bblack'] = monochrome_logs ? '' : "\033[1;30m"
- colorcodes['bred'] = monochrome_logs ? '' : "\033[1;31m"
- colorcodes['bgreen'] = monochrome_logs ? '' : "\033[1;32m"
- colorcodes['byellow'] = monochrome_logs ? '' : "\033[1;33m"
- colorcodes['bblue'] = monochrome_logs ? '' : "\033[1;34m"
- colorcodes['bpurple'] = monochrome_logs ? '' : "\033[1;35m"
- colorcodes['bcyan'] = monochrome_logs ? '' : "\033[1;36m"
- colorcodes['bwhite'] = monochrome_logs ? '' : "\033[1;37m"
-
- // Underline
- colorcodes['ublack'] = monochrome_logs ? '' : "\033[4;30m"
- colorcodes['ured'] = monochrome_logs ? '' : "\033[4;31m"
- colorcodes['ugreen'] = monochrome_logs ? '' : "\033[4;32m"
- colorcodes['uyellow'] = monochrome_logs ? '' : "\033[4;33m"
- colorcodes['ublue'] = monochrome_logs ? '' : "\033[4;34m"
- colorcodes['upurple'] = monochrome_logs ? '' : "\033[4;35m"
- colorcodes['ucyan'] = monochrome_logs ? '' : "\033[4;36m"
- colorcodes['uwhite'] = monochrome_logs ? '' : "\033[4;37m"
-
- // High Intensity
- colorcodes['iblack'] = monochrome_logs ? '' : "\033[0;90m"
- colorcodes['ired'] = monochrome_logs ? '' : "\033[0;91m"
- colorcodes['igreen'] = monochrome_logs ? '' : "\033[0;92m"
- colorcodes['iyellow'] = monochrome_logs ? '' : "\033[0;93m"
- colorcodes['iblue'] = monochrome_logs ? '' : "\033[0;94m"
- colorcodes['ipurple'] = monochrome_logs ? '' : "\033[0;95m"
- colorcodes['icyan'] = monochrome_logs ? '' : "\033[0;96m"
- colorcodes['iwhite'] = monochrome_logs ? '' : "\033[0;97m"
-
- // Bold High Intensity
- colorcodes['biblack'] = monochrome_logs ? '' : "\033[1;90m"
- colorcodes['bired'] = monochrome_logs ? '' : "\033[1;91m"
- colorcodes['bigreen'] = monochrome_logs ? '' : "\033[1;92m"
- colorcodes['biyellow'] = monochrome_logs ? '' : "\033[1;93m"
- colorcodes['biblue'] = monochrome_logs ? '' : "\033[1;94m"
- colorcodes['bipurple'] = monochrome_logs ? '' : "\033[1;95m"
- colorcodes['bicyan'] = monochrome_logs ? '' : "\033[1;96m"
- colorcodes['biwhite'] = monochrome_logs ? '' : "\033[1;97m"
-
- return colorcodes
- }
-
- //
- // Does what is says on the tin
- //
- public static String dashedLine(monochrome_logs) {
- Map colors = logColours(monochrome_logs)
- return "-${colors.dim}----------------------------------------------------${colors.reset}-"
- }
-
- //
- // nf-core logo
- //
- public static String logo(workflow, monochrome_logs) {
- Map colors = logColours(monochrome_logs)
- String workflow_version = NfcoreTemplate.version(workflow)
- String.format(
- """\n
- ${dashedLine(monochrome_logs)}
- ${colors.green},--.${colors.black}/${colors.green},-.${colors.reset}
- ${colors.blue} ___ __ __ __ ___ ${colors.green}/,-._.--~\'${colors.reset}
- ${colors.blue} |\\ | |__ __ / ` / \\ |__) |__ ${colors.yellow}} {${colors.reset}
- ${colors.blue} | \\| | \\__, \\__/ | \\ |___ ${colors.green}\\`-._,-`-,${colors.reset}
- ${colors.green}`._,._,\'${colors.reset}
- ${colors.purple} ${workflow.manifest.name} ${workflow_version}${colors.reset}
- ${dashedLine(monochrome_logs)}
- """.stripIndent()
- )
- }
-}
diff --git a/lib/Utils.groovy b/lib/Utils.groovy
deleted file mode 100644
index 8d030f4e..00000000
--- a/lib/Utils.groovy
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-// This file holds several Groovy functions that could be useful for any Nextflow pipeline
-//
-
-import org.yaml.snakeyaml.Yaml
-
-class Utils {
-
- //
- // When running with -profile conda, warn if channels have not been set-up appropriately
- //
- public static void checkCondaChannels(log) {
- Yaml parser = new Yaml()
- def channels = []
- try {
- def config = parser.load("conda config --show channels".execute().text)
- channels = config.channels
- } catch(NullPointerException | IOException e) {
- log.warn "Could not verify conda channel configuration."
- return
- }
-
- // Check that all channels are present
- // This channel list is ordered by required channel priority.
- def required_channels_in_order = ['conda-forge', 'bioconda', 'defaults']
- def channels_missing = ((required_channels_in_order as Set) - (channels as Set)) as Boolean
-
- // Check that they are in the right order
- def channel_priority_violation = false
- def n = required_channels_in_order.size()
- for (int i = 0; i < n - 1; i++) {
- channel_priority_violation |= !(channels.indexOf(required_channels_in_order[i]) < channels.indexOf(required_channels_in_order[i+1]))
- }
-
- if (channels_missing | channel_priority_violation) {
- log.warn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " There is a problem with your Conda configuration!\n\n" +
- " You will need to set-up the conda-forge and bioconda channels correctly.\n" +
- " Please refer to https://bioconda.github.io/\n" +
- " The observed channel order is \n" +
- " ${channels}\n" +
- " but the following channel order is required:\n" +
- " ${required_channels_in_order}\n" +
- "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
- }
- }
-}
diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy
deleted file mode 100644
index 2f2008fb..00000000
--- a/lib/WorkflowMain.groovy
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// This file holds several functions specific to the main.nf workflow in the nf-core/mhcquant pipeline
-//
-
-import nextflow.Nextflow
-
-class WorkflowMain {
-
- //
- // Citation string for pipeline
- //
- public static String citation(workflow) {
- return "If you use ${workflow.manifest.name} for your analysis please cite:\n\n" +
- "* The pipeline publication\n" +
- " https://doi.org/10.1021/acs.jproteome.9b00313\n\n" +
- "* The pipeline\n" +
- " https://doi.org/10.5281/zenodo.1569909\n\n" +
- "* The nf-core framework\n" +
- " https://doi.org/10.1038/s41587-020-0439-x\n\n" +
- "* Software dependencies\n" +
- " https://github.com/${workflow.manifest.name}/blob/master/CITATIONS.md"
- }
-
-
- //
- // Validate parameters and print summary to screen
- //
- public static void initialise(workflow, params, log, args) {
-
- // Print workflow version and exit on --version
- if (params.version) {
- String workflow_version = NfcoreTemplate.version(workflow)
- log.info "${workflow.manifest.name} ${workflow_version}"
- System.exit(0)
- }
-
- // Check that a -profile or Nextflow config has been provided to run the pipeline
- NfcoreTemplate.checkConfigProvided(workflow, log)
- // Check that the profile doesn't contain spaces and doesn't end with a trailing comma
- checkProfile(workflow.profile, args, log)
-
- // Check that conda channels are set-up correctly
- if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) {
- Utils.checkCondaChannels(log)
- }
-
- // Check AWS batch settings
- NfcoreTemplate.awsBatch(workflow, params)
-
- // Check input has been provided
- if (!params.input) {
- log.error "Please provide an input samplesheet to the pipeline e.g. '--input samplesheet.tsv'"
- System.exit(1)
- }
- }
-
- //
- // Exit pipeline if --profile contains spaces
- //
- private static void checkProfile(profile, args, log) {
- if (profile.endsWith(',')) {
- Nextflow.error "Profile cannot end with a trailing comma. Please remove the comma from the end of the profile string.\nHint: A common mistake is to provide multiple values to `-profile` separated by spaces. Please use commas to separate profiles instead,e.g., `-profile docker,test`."
- }
- if (args[0]) {
- log.warn "nf-core pipelines do not accept positional arguments. The positional argument `${args[0]}` has been detected.\n Hint: A common mistake is to provide multiple values to `-profile` separated by spaces. Please use commas to separate profiles instead,e.g., `-profile docker,test`."
- }
- }
-}
diff --git a/lib/WorkflowMhcquant.groovy b/lib/WorkflowMhcquant.groovy
deleted file mode 100644
index a99924b3..00000000
--- a/lib/WorkflowMhcquant.groovy
+++ /dev/null
@@ -1,214 +0,0 @@
-//
-// This file holds several functions specific to the workflow/mhcquant.nf in the nf-core/mhcquant pipeline
-//
-
-import nextflow.Nextflow
-import groovy.text.SimpleTemplateEngine
-
-class WorkflowMhcquant {
-
- //
- // Check and validate parameters
- //
- public static void initialise(params, log) {
-
- // Error raise when the database fasta is not included
- if (!params.fasta) {
- log.error "No database fasta was provided, make sure you have used the '--fasta' option."
- System.exit(1)
- }
-
- // Warning for the outpout dir (when not defined)
- if (!params.outdir) {
- outdirWarn(log)
- }
-
- // Database options
- if (params.skip_decoy_generation) {
- skipDecoyWarn(log)
- }
-
- // FDR Scoring
- if (params.klammer && params.description_correct_features == 0) {
- klammerConflictWarn(log)
- }
-
- // Quantification options
- if (params.quantification_fdr) {
- enabledFDRWarn(log)
- }
-
- // Allele sheet
- if ( ( params.predict_class_1 || params.predict_class_2 ) && !params.allele_sheet) {
- // If no allele sheet is provided
- noAllelesError(log)
- System.exit(1)
- }
-
- if ( params.include_proteins_from_vcf && !params.vcf_sheet) {
- // If no vcf sheet is provided
- noVcfError(log)
- System.exit(1)
- }
- }
-
- //
- // Print a warning when the output directory is undefined
- //
- private static void outdirWarn(log) {
- log.warn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " Results into \'./results\'.\n" +
- " If you want to define a result directory, please use the --outdir option" +
- "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
- }
-
- //
- // Print a warning when the parameter skip_decoy_generation is used
- //
- private static void skipDecoyWarn(log) {
- log.warn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " Be aware: skipping decoy generation will prevent generating variants and subset FDR refinement\n" +
- " Decoys have to be named with DECOY_ as prefix in your fasta database\n" +
- "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
- }
-
- //
- // Print a warning when the parameter skip_decoy_generation is used
- //
- private static void klammerConflictWarn(log) {
- log.warn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " Quantification FDR enabled\n" +
- "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
- }
-
- //
- // Print a warning when the parameter skip_decoy_generation is used
- //
- private static void enabledFDRWarn(log) {
- log.warn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " Klammer was specified, but description of correct features was still 0\n."+
- " Please provide a description of correct features greater than 0.\n" +
- " Klammer has been turned off!\n" +
- "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
- }
-
- //
- // Print an error when the no allele_sheet is provided
- //
- private static void noAllelesError(log) {
- log.error "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " --predict_class_1 or --predict_class_2 was used but no allele sheet has been provided. \n" +
- " Make sure you have used the '--allele_sheet' option and include an allele sheet.\n" +
- "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
- }
-
- //
- // Print an error when there is no vcf_sheet provided
- //
- private static void noVcfError(log) {
- log.error "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " --include_proteins_from_vcf was used but no vcf sheet has been provided. \n" +
- " Make sure you have used the '--vcf_sheet' option and include an vcf sheet.\n" +
- "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
- }
-
- //
- // Get workflow summary for MultiQC
- //
- public static String paramsSummaryMultiqc(workflow, summary) {
- String summary_section = ''
- for (group in summary.keySet()) {
- def group_params = summary.get(group) // This gets the parameters of that particular group
- if (group_params) {
- summary_section += "
$group
\n"
- summary_section += " \n"
- for (param in group_params.keySet()) {
- summary_section += " - $param
- ${group_params.get(param) ?: 'N/A'}
\n"
- }
- summary_section += "
\n"
- }
- }
-
- String yaml_file_text = "id: '${workflow.manifest.name.replace('/','-')}-summary'\n"
- yaml_file_text += "description: ' - this information is collected when the pipeline is started.'\n"
- yaml_file_text += "section_name: '${workflow.manifest.name} Workflow Summary'\n"
- yaml_file_text += "section_href: 'https://github.com/${workflow.manifest.name}'\n"
- yaml_file_text += "plot_type: 'html'\n"
- yaml_file_text += "data: |\n"
- yaml_file_text += "${summary_section}"
- return yaml_file_text
- }
-
- //
- // Generate methods description for MultiQC
- //
-
- public static String toolCitationText(params) {
-
- // TODO nf-core: Optionally add in-text citation tools to this list.
- // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "Tool (Foo et al. 2023)" : "",
- // Uncomment function in methodsDescriptionText to render in MultiQC report
- def citation_text = [
- "Tools used in the workflow included:",
- "FastQC (Andrews 2010),",
- "MultiQC (Ewels et al. 2016)",
- "."
- ].join(' ').trim()
-
- return citation_text
- }
-
- public static String toolBibliographyText(params) {
-
- // TODO Optionally add bibliographic entries to this list.
- // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "Author (2023) Pub name, Journal, DOI" : "",
- // Uncomment function in methodsDescriptionText to render in MultiQC report
- def reference_text = [
- "Andrews S, (2010) FastQC, URL: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/).",
- "Ewels, P., Magnusson, M., Lundin, S., & Käller, M. (2016). MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics , 32(19), 3047–3048. doi: /10.1093/bioinformatics/btw354"
- ].join(' ').trim()
-
- return reference_text
- }
-
- public static String methodsDescriptionText(run_workflow, mqc_methods_yaml, params) {
- // Convert to a named map so can be used as with familar NXF ${workflow} variable syntax in the MultiQC YML file
- def meta = [:]
- meta.workflow = run_workflow.toMap()
- meta["manifest_map"] = run_workflow.manifest.toMap()
-
- // Pipeline DOI
- meta["doi_text"] = meta.manifest_map.doi ? "(doi: ${meta.manifest_map.doi})" : ""
- meta["nodoi_text"] = meta.manifest_map.doi ? "": "If available, make sure to update the text to include the Zenodo DOI of version of the pipeline used. "
-
- // Tool references
- meta["tool_citations"] = ""
- meta["tool_bibliography"] = ""
-
- // TODO Only uncomment below if logic in toolCitationText/toolBibliographyText has been filled!
- //meta["tool_citations"] = toolCitationText(params).replaceAll(", \\.", ".").replaceAll("\\. \\.", ".").replaceAll(", \\.", ".")
- //meta["tool_bibliography"] = toolBibliographyText(params)
-
-
- def methods_text = mqc_methods_yaml.text
-
- def engine = new SimpleTemplateEngine()
- def description_html = engine.createTemplate(methods_text).make(meta)
-
- return description_html
- }
-
- //
- // Exit pipeline if incorrect --genome key provided
- //
- private static void genomeExistsError(params, log) {
- if (params.genomes && params.genome && !params.genomes.containsKey(params.genome)) {
- def error_string = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " Genome '${params.genome}' not found in any config files provided to the pipeline.\n" +
- " Currently, the available genome keys are:\n" +
- " ${params.genomes.keySet().join(", ")}\n" +
- "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
- Nextflow.error(error_string)
- }
- }
-}
diff --git a/main.nf b/main.nf
index b654756f..a914619c 100644
--- a/main.nf
+++ b/main.nf
@@ -13,55 +13,86 @@ nextflow.enable.dsl = 2
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- VALIDATE & PRINT PARAMETER SUMMARY
+ IMPORT FUNCTIONS / MODULES / SUBWORKFLOWS / WORKFLOWS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-include { validateParameters; paramsHelp } from 'plugin/nf-validation'
-
-// Print help message if needed
-if (params.help) {
- def logo = NfcoreTemplate.logo(workflow, params.monochrome_logs)
- def citation = '\n' + WorkflowMain.citation(workflow) + '\n'
- def String command = "nextflow run ${workflow.manifest.name} --input samplesheet.csv --genome GRCh37 -profile docker"
- log.info logo + paramsHelp(command) + citation + NfcoreTemplate.dashedLine(params.monochrome_logs)
- System.exit(0)
-}
-
-// Validate input parameters
-if (params.validate_params) {
- validateParameters()
-}
-
-WorkflowMain.initialise(workflow, params, log, args)
+include { MHCQUANT } from './workflows/mhcquant'
+include { PIPELINE_INITIALISATION } from './subworkflows/local/utils_nfcore_mhcquant_pipeline'
+include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_mhcquant_pipeline'
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- NAMED WORKFLOW FOR PIPELINE
+ NAMED WORKFLOWS FOR PIPELINE
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-include { MHCQUANT } from './workflows/mhcquant'
-
//
-// WORKFLOW: Run main nf-core/mhcquant analysis pipeline
+// WORKFLOW: Run main analysis pipeline depending on type of input
//
workflow NFCORE_MHCQUANT {
- MHCQUANT ()
-}
+ take:
+ samplesheet // channel: samplesheet read in from --input
+ fasta // channel: reference database read in from --fasta
+
+ main:
+
+ //
+ // WORKFLOW: Run pipeline
+ //
+ MHCQUANT (
+ samplesheet,
+ fasta
+ )
+
+ emit:
+ multiqc_report = MHCQUANT.out.multiqc_report // channel: /path/to/multiqc_report.html
+
+}
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- RUN ALL WORKFLOWS
+ RUN MAIN WORKFLOW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-//
-// WORKFLOW: Execute a single named workflow for the pipeline
-// See: https://github.com/nf-core/rnaseq/issues/619
-//
workflow {
- NFCORE_MHCQUANT ()
+
+ main:
+
+ //
+ // SUBWORKFLOW: Run initialisation tasks
+ //
+ PIPELINE_INITIALISATION (
+ params.version,
+ params.help,
+ params.validate_params,
+ params.monochrome_logs,
+ args,
+ params.outdir,
+ params.input
+ )
+
+ //
+ // WORKFLOW: Run main workflow
+ //
+ NFCORE_MHCQUANT (
+ PIPELINE_INITIALISATION.out.samplesheet,
+ PIPELINE_INITIALISATION.out.fasta
+ )
+
+ //
+ // SUBWORKFLOW: Run completion tasks
+ //
+ PIPELINE_COMPLETION (
+ params.email,
+ params.email_on_fail,
+ params.plaintext_email,
+ params.outdir,
+ params.monochrome_logs,
+ params.hook_url,
+ NFCORE_MHCQUANT.out.multiqc_report
+ )
}
/*
diff --git a/modules.json b/modules.json
index a530ccd1..7ec99f62 100644
--- a/modules.json
+++ b/modules.json
@@ -5,14 +5,9 @@
"https://github.com/nf-core/modules.git": {
"modules": {
"nf-core": {
- "custom/dumpsoftwareversions": {
- "branch": "master",
- "git_sha": "8ec825f465b9c17f9d83000022995b4f7de6fe93",
- "installed_by": ["modules"]
- },
"multiqc": {
"branch": "master",
- "git_sha": "8ec825f465b9c17f9d83000022995b4f7de6fe93",
+ "git_sha": "ccacf6f5de6df3bc6d73b665c1fd2933d8bbc290",
"installed_by": ["modules"]
},
"openms/decoydatabase": {
@@ -51,6 +46,25 @@
"installed_by": ["modules"]
}
}
+ },
+ "subworkflows": {
+ "nf-core": {
+ "utils_nextflow_pipeline": {
+ "branch": "master",
+ "git_sha": "cd08c91373cd00a73255081340e4914485846ba1",
+ "installed_by": ["subworkflows"]
+ },
+ "utils_nfcore_pipeline": {
+ "branch": "master",
+ "git_sha": "262b17ed2aad591039f914951659177e6c39a8d8",
+ "installed_by": ["subworkflows"]
+ },
+ "utils_nfvalidation_plugin": {
+ "branch": "master",
+ "git_sha": "cd08c91373cd00a73255081340e4914485846ba1",
+ "installed_by": ["subworkflows"]
+ }
+ }
}
}
}
diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf
deleted file mode 100644
index 1f350859..00000000
--- a/modules/local/samplesheet_check.nf
+++ /dev/null
@@ -1,34 +0,0 @@
-process SAMPLESHEET_CHECK {
- tag "$samplesheet"
- label 'process_single'
-
- conda "conda-forge::python=3.8.3"
- container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
- 'https://depot.galaxyproject.org/singularity/python:3.8.3' :
- 'biocontainers/python:3.8.3' }"
-
- input:
- path samplesheet
-
- output:
- path '*.csv' , emit: csv
- path "versions.yml", emit: versions
-
- when:
- task.ext.when == null || task.ext.when
-
- when:
- task.ext.when == null || task.ext.when
-
- script: // This script is bundled with the pipeline, in nf-core/mhcquant/bin/
- """
- check_samplesheet.py \\
- $samplesheet \\
- samplesheet.valid.csv
-
- cat <<-END_VERSIONS > versions.yml
- "${task.process}":
- python: \$(python --version | sed 's/Python //g')
- END_VERSIONS
- """
-}
diff --git a/modules/nf-core/custom/dumpsoftwareversions/environment.yml b/modules/nf-core/custom/dumpsoftwareversions/environment.yml
deleted file mode 100644
index 9b3272bc..00000000
--- a/modules/nf-core/custom/dumpsoftwareversions/environment.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: custom_dumpsoftwareversions
-channels:
- - conda-forge
- - bioconda
- - defaults
-dependencies:
- - bioconda::multiqc=1.19
diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf
deleted file mode 100644
index f2187611..00000000
--- a/modules/nf-core/custom/dumpsoftwareversions/main.nf
+++ /dev/null
@@ -1,24 +0,0 @@
-process CUSTOM_DUMPSOFTWAREVERSIONS {
- label 'process_single'
-
- // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container
- conda "${moduleDir}/environment.yml"
- container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
- 'https://depot.galaxyproject.org/singularity/multiqc:1.19--pyhdfd78af_0' :
- 'biocontainers/multiqc:1.19--pyhdfd78af_0' }"
-
- input:
- path versions
-
- output:
- path "software_versions.yml" , emit: yml
- path "software_versions_mqc.yml", emit: mqc_yml
- path "versions.yml" , emit: versions
-
- when:
- task.ext.when == null || task.ext.when
-
- script:
- def args = task.ext.args ?: ''
- template 'dumpsoftwareversions.py'
-}
diff --git a/modules/nf-core/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/custom/dumpsoftwareversions/meta.yml
deleted file mode 100644
index 5f15a5fd..00000000
--- a/modules/nf-core/custom/dumpsoftwareversions/meta.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json
-name: custom_dumpsoftwareversions
-description: Custom module used to dump software versions within the nf-core pipeline template
-keywords:
- - custom
- - dump
- - version
-tools:
- - custom:
- description: Custom module used to dump software versions within the nf-core pipeline template
- homepage: https://github.com/nf-core/tools
- documentation: https://github.com/nf-core/tools
- licence: ["MIT"]
-input:
- - versions:
- type: file
- description: YML file containing software versions
- pattern: "*.yml"
-output:
- - yml:
- type: file
- description: Standard YML file containing software versions
- pattern: "software_versions.yml"
- - mqc_yml:
- type: file
- description: MultiQC custom content YML file containing software versions
- pattern: "software_versions_mqc.yml"
- - versions:
- type: file
- description: File containing software versions
- pattern: "versions.yml"
-authors:
- - "@drpatelh"
- - "@grst"
-maintainers:
- - "@drpatelh"
- - "@grst"
diff --git a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py
deleted file mode 100755
index e55b8d43..00000000
--- a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env python
-
-
-"""Provide functions to merge multiple versions.yml files."""
-
-
-import platform
-from textwrap import dedent
-
-import yaml
-
-
-def _make_versions_html(versions):
- """Generate a tabular HTML output of all versions for MultiQC."""
- html = [
- dedent(
- """\\
-
-
-
-
- Process Name |
- Software |
- Version |
-
-
- """
- )
- ]
- for process, tmp_versions in sorted(versions.items()):
- html.append("")
- for i, (tool, version) in enumerate(sorted(tmp_versions.items())):
- html.append(
- dedent(
- f"""\\
-
- {process if (i == 0) else ''} |
- {tool} |
- {version} |
-
- """
- )
- )
- html.append("")
- html.append("
")
- return "\\n".join(html)
-
-
-def main():
- """Load all version files and generate merged output."""
- versions_this_module = {}
- versions_this_module["${task.process}"] = {
- "python": platform.python_version(),
- "yaml": yaml.__version__,
- }
-
- with open("$versions") as f:
- versions_by_process = yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module
-
- # aggregate versions by the module name (derived from fully-qualified process name)
- versions_by_module = {}
- for process, process_versions in versions_by_process.items():
- module = process.split(":")[-1]
- try:
- if versions_by_module[module] != process_versions:
- raise AssertionError(
- "We assume that software versions are the same between all modules. "
- "If you see this error-message it means you discovered an edge-case "
- "and should open an issue in nf-core/tools. "
- )
- except KeyError:
- versions_by_module[module] = process_versions
-
- versions_by_module["Workflow"] = {
- "Nextflow": "$workflow.nextflow.version",
- "$workflow.manifest.name": "$workflow.manifest.version",
- }
-
- versions_mqc = {
- "id": "software_versions",
- "section_name": "${workflow.manifest.name} Software Versions",
- "section_href": "https://github.com/${workflow.manifest.name}",
- "plot_type": "html",
- "description": "are collected at run time from the software output.",
- "data": _make_versions_html(versions_by_module),
- }
-
- with open("software_versions.yml", "w") as f:
- yaml.dump(versions_by_module, f, default_flow_style=False)
- with open("software_versions_mqc.yml", "w") as f:
- yaml.dump(versions_mqc, f, default_flow_style=False)
-
- with open("versions.yml", "w") as f:
- yaml.dump(versions_this_module, f, default_flow_style=False)
-
-
-if __name__ == "__main__":
- main()
diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test
deleted file mode 100644
index b1e1630b..00000000
--- a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test
+++ /dev/null
@@ -1,43 +0,0 @@
-nextflow_process {
-
- name "Test Process CUSTOM_DUMPSOFTWAREVERSIONS"
- script "../main.nf"
- process "CUSTOM_DUMPSOFTWAREVERSIONS"
- tag "modules"
- tag "modules_nfcore"
- tag "custom"
- tag "dumpsoftwareversions"
- tag "custom/dumpsoftwareversions"
-
- test("Should run without failures") {
- when {
- process {
- """
- def tool1_version = '''
- TOOL1:
- tool1: 0.11.9
- '''.stripIndent()
-
- def tool2_version = '''
- TOOL2:
- tool2: 1.9
- '''.stripIndent()
-
- input[0] = Channel.of(tool1_version, tool2_version).collectFile()
- """
- }
- }
-
- then {
- assertAll(
- { assert process.success },
- { assert snapshot(
- process.out.versions,
- file(process.out.mqc_yml[0]).readLines()[0..10],
- file(process.out.yml[0]).readLines()[0..7]
- ).match()
- }
- )
- }
- }
-}
diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap
deleted file mode 100644
index 5f59a936..00000000
--- a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "Should run without failures": {
- "content": [
- [
- "versions.yml:md5,76d454d92244589d32455833f7c1ba6d"
- ],
- [
- "data: \"\\n\\n \\n \\n Process Name | \\n \\",
- " \\ Software | \\n Version | \\n
\\n \\n\\",
- " \\n\\n\\n CUSTOM_DUMPSOFTWAREVERSIONS | \\n python | \\n\\",
- " \\ 3.11.7 | \\n
\\n\\n\\n | \\n \\",
- " \\ yaml | \\n 5.4.1 | \\n
\\n\\n\\n\\",
- " \\n\\n TOOL1 | \\n tool1 | \\n\\",
- " \\ 0.11.9 | \\n
\\n\\n\\n\\n\\n TOOL2 | \\n\\",
- " \\ tool2 | \\n 1.9 | \\n
\\n\\n\\n\\",
- " \\n\\n Workflow | \\n Nextflow | \\n\\"
- ],
- [
- "CUSTOM_DUMPSOFTWAREVERSIONS:",
- " python: 3.11.7",
- " yaml: 5.4.1",
- "TOOL1:",
- " tool1: 0.11.9",
- "TOOL2:",
- " tool2: '1.9'",
- "Workflow:"
- ]
- ],
- "timestamp": "2024-01-09T23:01:18.710682"
- }
-}
\ No newline at end of file
diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml
deleted file mode 100644
index 405aa24a..00000000
--- a/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-custom/dumpsoftwareversions:
- - modules/nf-core/custom/dumpsoftwareversions/**
diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/multiqc/environment.yml
index 7625b752..2212096a 100644
--- a/modules/nf-core/multiqc/environment.yml
+++ b/modules/nf-core/multiqc/environment.yml
@@ -4,4 +4,4 @@ channels:
- bioconda
- defaults
dependencies:
- - bioconda::multiqc=1.19
+ - bioconda::multiqc=1.20
diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf
index 1b9f7c43..354f4430 100644
--- a/modules/nf-core/multiqc/main.nf
+++ b/modules/nf-core/multiqc/main.nf
@@ -3,8 +3,8 @@ process MULTIQC {
conda "${moduleDir}/environment.yml"
container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
- 'https://depot.galaxyproject.org/singularity/multiqc:1.19--pyhdfd78af_0' :
- 'biocontainers/multiqc:1.19--pyhdfd78af_0' }"
+ 'https://depot.galaxyproject.org/singularity/multiqc:1.20--pyhdfd78af_0' :
+ 'biocontainers/multiqc:1.20--pyhdfd78af_0' }"
input:
path multiqc_files, stageAs: "?/*"
diff --git a/modules/nf-core/multiqc/tests/main.nf.test b/modules/nf-core/multiqc/tests/main.nf.test
index d0438eda..f1c4242e 100644
--- a/modules/nf-core/multiqc/tests/main.nf.test
+++ b/modules/nf-core/multiqc/tests/main.nf.test
@@ -3,6 +3,7 @@ nextflow_process {
name "Test Process MULTIQC"
script "../main.nf"
process "MULTIQC"
+
tag "modules"
tag "modules_nfcore"
tag "multiqc"
@@ -12,7 +13,7 @@ nextflow_process {
when {
process {
"""
- input[0] = Channel.of([file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz_fastqc_zip'], checkIfExists: true)])
+ input[0] = Channel.of(file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastqc/test_fastqc.zip', checkIfExists: true))
input[1] = []
input[2] = []
input[3] = []
@@ -25,7 +26,7 @@ nextflow_process {
{ assert process.success },
{ assert process.out.report[0] ==~ ".*/multiqc_report.html" },
{ assert process.out.data[0] ==~ ".*/multiqc_data" },
- { assert snapshot(process.out.versions).match("versions") }
+ { assert snapshot(process.out.versions).match("multiqc_versions_single") }
)
}
@@ -36,7 +37,7 @@ nextflow_process {
when {
process {
"""
- input[0] = Channel.of([file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz_fastqc_zip'], checkIfExists: true)])
+ input[0] = Channel.of(file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastqc/test_fastqc.zip', checkIfExists: true))
input[1] = Channel.of(file("https://github.com/nf-core/tools/raw/dev/nf_core/pipeline-template/assets/multiqc_config.yml", checkIfExists: true))
input[2] = []
input[3] = []
@@ -49,7 +50,7 @@ nextflow_process {
{ assert process.success },
{ assert process.out.report[0] ==~ ".*/multiqc_report.html" },
{ assert process.out.data[0] ==~ ".*/multiqc_data" },
- { assert snapshot(process.out.versions).match("versions") }
+ { assert snapshot(process.out.versions).match("multiqc_versions_config") }
)
}
}
@@ -61,7 +62,7 @@ nextflow_process {
when {
process {
"""
- input[0] = Channel.of([file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz_fastqc_zip'], checkIfExists: true)])
+ input[0] = Channel.of(file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastqc/test_fastqc.zip', checkIfExists: true))
input[1] = []
input[2] = []
input[3] = []
@@ -75,7 +76,7 @@ nextflow_process {
{ assert snapshot(process.out.report.collect { file(it).getName() } +
process.out.data.collect { file(it).getName() } +
process.out.plots.collect { file(it).getName() } +
- process.out.versions ).match() }
+ process.out.versions ).match("multiqc_stub") }
)
}
diff --git a/modules/nf-core/multiqc/tests/main.nf.test.snap b/modules/nf-core/multiqc/tests/main.nf.test.snap
index d37e7304..c204b488 100644
--- a/modules/nf-core/multiqc/tests/main.nf.test.snap
+++ b/modules/nf-core/multiqc/tests/main.nf.test.snap
@@ -1,21 +1,41 @@
{
- "versions": {
+ "multiqc_versions_single": {
"content": [
[
- "versions.yml:md5,14e9a2661241abd828f4f06a7b5c222d"
+ "versions.yml:md5,d320d4c37e349c5588e07e7a31cd4186"
]
],
- "timestamp": "2024-01-09T23:02:49.911994"
+ "meta": {
+ "nf-test": "0.8.4",
+ "nextflow": "23.10.1"
+ },
+ "timestamp": "2024-02-14T09:28:51.744211298"
},
- "sarscov2 single-end [fastqc] - stub": {
+ "multiqc_stub": {
"content": [
[
"multiqc_report.html",
"multiqc_data",
"multiqc_plots",
- "versions.yml:md5,14e9a2661241abd828f4f06a7b5c222d"
+ "versions.yml:md5,d320d4c37e349c5588e07e7a31cd4186"
]
],
- "timestamp": "2024-01-09T23:03:14.524346"
+ "meta": {
+ "nf-test": "0.8.4",
+ "nextflow": "23.10.1"
+ },
+ "timestamp": "2024-02-14T09:29:28.847433492"
+ },
+ "multiqc_versions_config": {
+ "content": [
+ [
+ "versions.yml:md5,d320d4c37e349c5588e07e7a31cd4186"
+ ]
+ ],
+ "meta": {
+ "nf-test": "0.8.4",
+ "nextflow": "23.10.1"
+ },
+ "timestamp": "2024-02-14T09:29:13.223621555"
}
}
\ No newline at end of file
diff --git a/modules/nf-core/openms/idfilter/main.nf b/modules/nf-core/openms/idfilter/main.nf
index fdc9c13f..7bf16569 100644
--- a/modules/nf-core/openms/idfilter/main.nf
+++ b/modules/nf-core/openms/idfilter/main.nf
@@ -4,8 +4,8 @@ process OPENMS_IDFILTER {
conda "bioconda::openms=3.1.0"
container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
- 'https://depot.galaxyproject.org/singularity/openms:3.1.0--h8964181_3' :
- 'biocontainers/openms:3.1.0--h8964181_3' }"
+ 'https://depot.galaxyproject.org/singularity/openms:3.1.0--h8964181_2' :
+ 'biocontainers/openms:3.1.0--h8964181_2' }"
input:
tuple val(meta), path(id_file), path(filter_file)
diff --git a/nextflow.config b/nextflow.config
index ea7a0f65..e126464d 100644
--- a/nextflow.config
+++ b/nextflow.config
@@ -76,12 +76,11 @@ params {
deeplc_calibration_set_size = 0.15
// MultiQC options
- skip_multiqc = false
- multiqc_config = null
- multiqc_title = null
- multiqc_logo = null
- max_multiqc_email_size = '25.MB'
- multiqc_methods_description = null
+ multiqc_config = null
+ multiqc_title = null
+ multiqc_logo = null
+ max_multiqc_email_size = '25.MB'
+ multiqc_methods_description = null
// Boilerplate options
publish_dir_mode = 'copy'
@@ -101,7 +100,6 @@ params {
config_profile_contact = null
config_profile_url = null
-
// Max resource options
// Defaults only, expecting to be overwritten
max_memory = '128.GB'
diff --git a/nextflow_schema.json b/nextflow_schema.json
index 9ee2aa80..5ebbcc7f 100644
--- a/nextflow_schema.json
+++ b/nextflow_schema.json
@@ -18,10 +18,9 @@
"help_text": "Use this to specify a sample sheet table including your input raw or mzml files as well as their meta information such as SampleID and Condition. For example:\n\n| ID | Sample | Condition | ReplicateFileName |\n| -----|:------------:| ----------:|------------------------------------------:|\n| 1 | MM15_Melanom | A | data/MM15_Melanom_W_1_A_standard.raw |\n| 2 | MM15_Melanom | B | data/MM15_Melanom_W_1_B_standard.raw |\n| 3 | MM17_Melanom | B | data/MM17_Melanom_W_1_B_standard.raw |\n\n```bash\n--input 'path/samples.tsv'\n```",
"format": "file-path",
"exists": true,
- "mimetype": "text/csv",
- "pattern": "^\\S+\\.tsv$",
"schema": "assets/schema_input.json",
- "fa_icon": "fas fa-file-csv"
+ "mimetype": "text/csv",
+ "pattern": "^\\S+\\.tsv$"
},
"outdir": {
"type": "string",
@@ -457,13 +456,6 @@
"hidden": true,
"fa_icon": "fas fa-users-cog"
},
- "skip_multiqc": {
- "type": "boolean",
- "description": "Skip MultiQC.",
- "fa_icon": "fas fa-fast-forward",
- "hidden": true,
- "default": false
- },
"custom_config_base": {
"type": "string",
"description": "Base directory for Institutional configs.",
diff --git a/pyproject.toml b/pyproject.toml
index 7d08e1c8..56110621 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -3,11 +3,13 @@
[tool.ruff]
line-length = 120
target-version = "py38"
-select = ["I", "E1", "E4", "E7", "E9", "F", "UP", "N"]
cache-dir = "~/.cache/ruff"
-[tool.ruff.isort]
+[tool.ruff.lint]
+select = ["I", "E1", "E4", "E7", "E9", "F", "UP", "N"]
+
+[tool.ruff.lint.isort]
known-first-party = ["nf_core"]
-[tool.ruff.per-file-ignores]
+[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["E402", "F401"]
diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf
deleted file mode 100644
index 835d7ae0..00000000
--- a/subworkflows/local/input_check.nf
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-// Check input samplesheet and get read channels
-//
-
-include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check'
-
-workflow INPUT_CHECK {
- take:
- samplesheet // file: /path/to/samplesheet.csv
-
- main:
- // Process the samplesheet and get the initial channels
- def initial_channels = SAMPLESHEET_CHECK ( samplesheet )
- .csv
- .splitCsv(header:true, sep:'\t')
- .map { row -> [row.Sample + '_' + row.Condition, row] }
-
- // Group by sample_condition and count
- def count_channels = initial_channels
- .groupTuple()
- .map { key, rows -> [rows, rows.size()] }
- .flatMap { rows, count -> rows.collect { [it, count] } }
-
- ms_runs = count_channels.map { row, group_count -> create_ms_channel(row, group_count) }
-
- emit:
- ms_runs // channel: [ val(meta), [ runs ] ]
- versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ]
-}
-
-// Function to get list of [ meta, filenames ]
-def create_ms_channel(LinkedHashMap row, int group_count) {
- def meta = [:]
-
- meta.id = row.ID
- meta.sample = row.Sample
- meta.condition = row.Condition
- meta.ext = row.Extension
- meta.group_count = group_count
-
- // add path(s) of the data file(s) to the meta map
- def ms_file = file(row.ReplicateFileName)
- if (!ms_file.exists()) {
- exit 1, "ERROR: Please check input samplesheet -> MS file does not exist!\n${row.ReplicateFileName}"
- }
-
- meta.spectra = ms_file.baseName
- ms_meta = [ meta, [ ms_file ] ]
-
- return ms_meta
-}
diff --git a/subworkflows/local/process_feature.nf b/subworkflows/local/process_feature.nf
index 0e55e618..2b20b5f4 100644
--- a/subworkflows/local/process_feature.nf
+++ b/subworkflows/local/process_feature.nf
@@ -15,7 +15,7 @@ workflow PROCESS_FEATURE {
// Quantify identifications using targeted feature extraction
OPENMS_FEATUREFINDERIDENTIFICATION(ch_runs_to_be_quantified).featurexml
- .map { meta, featurexml -> [ groupKey([id: meta.sample + '_' + meta.condition], meta.group_count), featurexml] }
+ .map { meta, featurexml -> [ groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count), featurexml] }
.groupTuple()
.set { ch_features_grouped }
ch_versions = ch_versions.mix(OPENMS_FEATUREFINDERIDENTIFICATION.out.versions)
diff --git a/subworkflows/local/quant.nf b/subworkflows/local/quant.nf
index f72a98a5..a7dda954 100644
--- a/subworkflows/local/quant.nf
+++ b/subworkflows/local/quant.nf
@@ -50,14 +50,14 @@ workflow QUANT {
// Manipulate such that [meta_run1, idxml_run1, pout_group1], [meta_run2, idxml_run2, pout_group1] ...
ch_runs_score_switched
// Nextflow can only combine/join on the exact groupKey object, merge_id is not sufficient
- .map { meta, idxml -> [groupKey([id: meta.sample + '_' + meta.condition], meta.group_count) , meta, idxml] }
+ .map { meta, idxml -> [groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count) , meta, idxml] }
.combine(filter_q_value, by:0)
.map { group_meta, meta, idxml, q_value -> [meta, idxml, q_value] }
.set { ch_runs_to_filter}
// Filter runs based on fdr filtered coprocessed percolator output.
OPENMS_IDFILTER_QUANT( ch_runs_to_filter ).filtered
- .map { meta, idxml -> [ groupKey([id:meta.sample + '_' + meta.condition], meta.group_count), idxml] }
+ .map { meta, idxml -> [ groupKey([id:"${meta.sample}_${meta.condition}"], meta.group_count), idxml] }
.groupTuple()
.set { ch_runs_to_be_aligned }
ch_versions = ch_versions.mix(OPENMS_IDFILTER_QUANT.out.versions)
@@ -72,14 +72,14 @@ workflow QUANT {
// We need to merge groupwise the aligned idxml files together to use them as id_ext in featurefinder
OPENMS_IDMERGER_QUANT( MAP_ALIGNMENT.out.aligned_idxml
- .map { meta, aligned_idxml -> [ groupKey([id: meta.sample + '_' + meta.condition], meta.group_count), aligned_idxml] }
+ .map { meta, aligned_idxml -> [ groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count), aligned_idxml] }
.groupTuple())
ch_versions = ch_versions.mix(OPENMS_IDMERGER_QUANT.out.versions)
// Manipulate channels such that we end up with : [meta, mzml, run_idxml, merged_runs_idxml]
MAP_ALIGNMENT.out.aligned_mzml
.join( MAP_ALIGNMENT.out.aligned_idxml )
- .map { meta, mzml, idxml -> [ groupKey([id: meta.sample + '_' + meta.condition], meta.group_count), meta, mzml, idxml] }
+ .map { meta, mzml, idxml -> [ groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count), meta, mzml, idxml] }
.groupTuple()
.map { group_meta, meta, mzml, idxml -> [group_meta, meta, mzml, idxml] }
.join( OPENMS_IDMERGER_QUANT.out.idxml )
diff --git a/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf b/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf
new file mode 100644
index 00000000..42a110f2
--- /dev/null
+++ b/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf
@@ -0,0 +1,244 @@
+//
+// Subworkflow with functionality specific to the nf-core/pipeline pipeline
+//
+
+/*
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ IMPORT FUNCTIONS / MODULES / SUBWORKFLOWS
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+include { UTILS_NFVALIDATION_PLUGIN } from '../../nf-core/utils_nfvalidation_plugin'
+include { paramsSummaryMap } from 'plugin/nf-validation'
+include { fromSamplesheet } from 'plugin/nf-validation'
+include { UTILS_NEXTFLOW_PIPELINE } from '../../nf-core/utils_nextflow_pipeline'
+include { completionEmail } from '../../nf-core/utils_nfcore_pipeline'
+include { completionSummary } from '../../nf-core/utils_nfcore_pipeline'
+include { dashedLine } from '../../nf-core/utils_nfcore_pipeline'
+include { nfCoreLogo } from '../../nf-core/utils_nfcore_pipeline'
+include { imNotification } from '../../nf-core/utils_nfcore_pipeline'
+include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline'
+include { workflowCitation } from '../../nf-core/utils_nfcore_pipeline'
+
+/*
+========================================================================================
+ SUBWORKFLOW TO INITIALISE PIPELINE
+========================================================================================
+*/
+
+workflow PIPELINE_INITIALISATION {
+
+ take:
+ version // boolean: Display version and exit
+ help // boolean: Display help text
+ validate_params // boolean: Boolean whether to validate parameters against the schema at runtime
+ monochrome_logs // boolean: Do not use coloured log outputs
+ nextflow_cli_args // array: List of positional nextflow CLI args
+ outdir // string: The output directory where the results will be saved
+ input // string: Path to input samplesheet
+
+ main:
+
+ ch_versions = Channel.empty()
+
+ //
+ // Print version and exit if required and dump pipeline parameters to JSON file
+ //
+ UTILS_NEXTFLOW_PIPELINE (
+ version,
+ true,
+ outdir,
+ workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1
+ )
+
+ //
+ // Validate parameters and generate parameter summary to stdout
+ //
+ pre_help_text = nfCoreLogo(monochrome_logs)
+ post_help_text = '\n' + workflowCitation() + '\n' + dashedLine(monochrome_logs)
+ def String workflow_command = "nextflow run ${workflow.manifest.name} -profile --input samplesheet.csv --outdir "
+ UTILS_NFVALIDATION_PLUGIN (
+ help,
+ workflow_command,
+ pre_help_text,
+ post_help_text,
+ validate_params,
+ "nextflow_schema.json"
+ )
+
+ //
+ // Check config provided to the pipeline
+ //
+ UTILS_NFCORE_PIPELINE (
+ nextflow_cli_args
+ )
+
+ //
+ // Create channel from input file provided through params.input
+ //
+ Channel
+ .fromSamplesheet("input")
+ .map { meta, file -> [meta.subMap('sample','condition'), meta, file] }
+ .tap { ch_input }
+ .groupTuple()
+ // get number of files per sample-condition
+ .map { group_meta, metas, files -> [ group_meta, files.size()] }
+ .combine( ch_input, by:0 )
+ .map { group_meta, group_count, meta, file -> [meta + ['group_count':group_count, 'spectra':file.baseName, 'ext':file.getExtension().toLowerCase()], file] }
+ .set { ch_samplesheet }
+
+ //
+ // Create channel from the mandatory reference_database through params.fasta
+ //
+ Channel.fromPath(params.fasta, checkIfExists: true)
+ .map { fasta -> [[id:fasta.getBaseName()], fasta] }
+ .set { ch_fasta }
+
+ emit:
+ samplesheet = ch_samplesheet
+ fasta = ch_fasta
+}
+
+/*
+========================================================================================
+ SUBWORKFLOW FOR PIPELINE COMPLETION
+========================================================================================
+*/
+
+workflow PIPELINE_COMPLETION {
+
+ take:
+ email // string: email address
+ email_on_fail // string: email address sent on pipeline failure
+ plaintext_email // boolean: Send plain-text email instead of HTML
+ outdir // path: Path to output directory where results will be published
+ monochrome_logs // boolean: Disable ANSI colour codes in log output
+ hook_url // string: hook URL for notifications
+ multiqc_report // string: Path to MultiQC report
+
+ main:
+
+ summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json")
+
+ //
+ // Completion email and summary
+ //
+ workflow.onComplete {
+ if (email || email_on_fail) {
+ completionEmail(summary_params, email, email_on_fail, plaintext_email, outdir, monochrome_logs, multiqc_report.toList())
+ }
+
+ completionSummary(monochrome_logs)
+
+ if (hook_url) {
+ imNotification(summary_params, hook_url)
+ }
+ }
+}
+
+/*
+========================================================================================
+ FUNCTIONS
+========================================================================================
+*/
+//
+// Validate channels from input samplesheet
+//
+// Keeping this as an example for future samplesheet checks if additional fields are added (e.g. alleles)
+def validateInputSamplesheet(input) {
+ def (metas, fastqs) = input[1..2]
+
+ // Check that multiple runs of the same sample are of the same datatype i.e. single-end / paired-end
+ def endedness_ok = metas.collect{ it.single_end }.unique().size == 1
+ if (!endedness_ok) {
+ error("Please check input samplesheet -> Multiple runs of a sample must be of the same datatype i.e. single-end or paired-end: ${metas[0].id}")
+ }
+
+ return [ metas[0], fastqs ]
+}
+
+// MHC affinity prediction
+if (params.predict_class_1 || params.predict_class_2) {
+ Channel.from(file(params.allele_sheet, checkIfExists: true))
+ .splitCsv(header: true, sep:'\t')
+ .multiMap { col ->
+ classI: ["${col.Sample}", "${col.HLA_Alleles_Class_1}"]
+ classII: ["${col.Sample}", "${col.HLA_Alleles_Class_2}"] }
+ .set { ch_alleles_from_sheet }
+
+ // Allele class 1
+ if (params.predict_class_1) {
+ ch_alleles_from_sheet.classI
+ .ifEmpty { exit 1, "params.allele_sheet was empty - no allele input file supplied" }
+ .flatMap { it -> [tuple(it[0].toString(), it[1])] }
+ .set { peptides_class_1_alleles }
+ }
+
+ // Allele class 2
+ if (params.predict_class_2) {
+ ch_alleles_from_sheet.classII
+ .ifEmpty { exit 1, "params.allele_sheet was empty - no allele input file supplied" }
+ .flatMap { it -> [tuple(it[0].toString(), it[1])] }
+ .set { peptides_class_2_alleles }
+ }
+}
+
+//
+// Generate methods description for MultiQC
+//
+def toolCitationText() {
+ // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "Tool (Foo et al. 2023)" : "",
+ def citation_text = [
+ "Tools used in the workflow included:",
+ "OpenMS (Pfeuffer et al. 2024),",
+ "DeepLC (Bouwmeester et al. 2021)",
+ "MS²PIP (Declercq et al. 2023)",
+ "MS²Rescore (Declercq et al. 2022)",
+ "Percolator (Käll et al. 2007)",
+ "MapAligner (Weisser et al. 2013)",
+ "FeatureFinder (Weisser et al. 2017)",
+ "MultiQC (Ewels et al. 2016)",
+ "."
+ ].join(' ').trim()
+
+ return citation_text
+}
+
+def toolBibliographyText() {
+ // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "Author (2023) Pub name, Journal, DOI" : "",
+ def reference_text = [
+ "Pfeuffer, J., Bielow, C., Wein, S. et al. OpenMS 3 enables reproducible analysis of large-scale mass spectrometry data. Nat Methods (2024). doi: /10.1038/s41592-024-02197-7.",
+ "Eng JK., Hoopman MR., Jahan, TA. et al. A Deeper Look into Comet—Implementation and Features. J. Am. Soc. Mass Spectrom. 2015, 26, 11, 1865–1874 (2015). doi: /10.1007/s13361-015-1179-x.",
+ "Bouwmeester, R., Gabriels, R., Hulstaert, N. et al. DeepLC can predict retention times for peptides that carry as-yet unseen modifications. Nat Methods 18, 1363–1369 (2021). doi: /10.1038/s41592-021-01301-5",
+ "Declercq A, Bouwmeester R, Chiva C, et al. Updated MS²PIP web server supports cutting-edge proteomics applications. Nucleic Acids Res. 2023 Jul 5;51(W1):W338-W342. doi: /10.1093/nar/gkad335",
+ "Declercq A, Bouwmeester R, Hirschler A, Carapito C et al. MS2Rescore: Data-Driven Rescoring Dramatically Boosts Immunopeptide Identification Rates. Mol Cell Proteomics. 2022 Aug;21(8):100266. doi: /10.1016/j.mcpro.2022.100266",
+ "Käll, L., Canterbury, J., Weston, J. et al. Semi-supervised learning for peptide identification from shotgun proteomics datasets. Nat Methods 4, 923–925 (2007). doi: /10.1038/nmeth1113",
+ "Hendrik Weisser, Sven Nahnsen, Jonas Grossmann et al. An Automated Pipeline for High-Throughput Label-Free Quantitative Proteomics. Journal of Proteome Research 2013 12 (4), 1628-1644. doi: 10.1021/pr300992u",
+ "Hendrik Weisser and Jyoti S. Choudhary, Journal of Proteome Research 2017 16 (8), 2964-2974. doi: /10.1021/acs.jproteome.7b00248",
+ "Ewels, P., Magnusson, M., Lundin, S., & Käller, M. (2016). MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics , 32(19), 3047–3048. doi: /10.1093/bioinformatics/btw354"
+ ].join(' ').trim()
+
+ return reference_text
+}
+
+def methodsDescriptionText(mqc_methods_yaml) {
+ // Convert to a named map so can be used as with familar NXF ${workflow} variable syntax in the MultiQC YML file
+ def meta = [:]
+ meta.workflow = workflow.toMap()
+ meta["manifest_map"] = workflow.manifest.toMap()
+
+ // Pipeline DOI
+ meta["doi_text"] = meta.manifest_map.doi ? "(doi: ${meta.manifest_map.doi})" : ""
+ meta["nodoi_text"] = meta.manifest_map.doi ? "": "If available, make sure to update the text to include the Zenodo DOI of version of the pipeline used. "
+
+ meta["tool_citations"] = toolCitationText().replaceAll(", \\.", ".").replaceAll("\\. \\.", ".").replaceAll(", \\.", ".")
+ meta["tool_bibliography"] = toolBibliographyText()
+
+
+ def methods_text = mqc_methods_yaml.text
+
+ def engine = new groovy.text.SimpleTemplateEngine()
+ def description_html = engine.createTemplate(methods_text).make(meta)
+
+ return description_html.toString()
+}
diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf
new file mode 100644
index 00000000..ac31f28f
--- /dev/null
+++ b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf
@@ -0,0 +1,126 @@
+//
+// Subworkflow with functionality that may be useful for any Nextflow pipeline
+//
+
+import org.yaml.snakeyaml.Yaml
+import groovy.json.JsonOutput
+import nextflow.extension.FilesEx
+
+/*
+========================================================================================
+ SUBWORKFLOW DEFINITION
+========================================================================================
+*/
+
+workflow UTILS_NEXTFLOW_PIPELINE {
+
+ take:
+ print_version // boolean: print version
+ dump_parameters // boolean: dump parameters
+ outdir // path: base directory used to publish pipeline results
+ check_conda_channels // boolean: check conda channels
+
+ main:
+
+ //
+ // Print workflow version and exit on --version
+ //
+ if (print_version) {
+ log.info "${workflow.manifest.name} ${getWorkflowVersion()}"
+ System.exit(0)
+ }
+
+ //
+ // Dump pipeline parameters to a JSON file
+ //
+ if (dump_parameters && outdir) {
+ dumpParametersToJSON(outdir)
+ }
+
+ //
+ // When running with Conda, warn if channels have not been set-up appropriately
+ //
+ if (check_conda_channels) {
+ checkCondaChannels()
+ }
+
+ emit:
+ dummy_emit = true
+}
+
+/*
+========================================================================================
+ FUNCTIONS
+========================================================================================
+*/
+
+//
+// Generate version string
+//
+def getWorkflowVersion() {
+ String version_string = ""
+ if (workflow.manifest.version) {
+ def prefix_v = workflow.manifest.version[0] != 'v' ? 'v' : ''
+ version_string += "${prefix_v}${workflow.manifest.version}"
+ }
+
+ if (workflow.commitId) {
+ def git_shortsha = workflow.commitId.substring(0, 7)
+ version_string += "-g${git_shortsha}"
+ }
+
+ return version_string
+}
+
+//
+// Dump pipeline parameters to a JSON file
+//
+def dumpParametersToJSON(outdir) {
+ def timestamp = new java.util.Date().format( 'yyyy-MM-dd_HH-mm-ss')
+ def filename = "params_${timestamp}.json"
+ def temp_pf = new File(workflow.launchDir.toString(), ".${filename}")
+ def jsonStr = JsonOutput.toJson(params)
+ temp_pf.text = JsonOutput.prettyPrint(jsonStr)
+
+ FilesEx.copyTo(temp_pf.toPath(), "${outdir}/pipeline_info/params_${timestamp}.json")
+ temp_pf.delete()
+}
+
+//
+// When running with -profile conda, warn if channels have not been set-up appropriately
+//
+def checkCondaChannels() {
+ Yaml parser = new Yaml()
+ def channels = []
+ try {
+ def config = parser.load("conda config --show channels".execute().text)
+ channels = config.channels
+ } catch(NullPointerException | IOException e) {
+ log.warn "Could not verify conda channel configuration."
+ return
+ }
+
+ // Check that all channels are present
+ // This channel list is ordered by required channel priority.
+ def required_channels_in_order = ['conda-forge', 'bioconda', 'defaults']
+ def channels_missing = ((required_channels_in_order as Set) - (channels as Set)) as Boolean
+
+ // Check that they are in the right order
+ def channel_priority_violation = false
+ def n = required_channels_in_order.size()
+ for (int i = 0; i < n - 1; i++) {
+ channel_priority_violation |= !(channels.indexOf(required_channels_in_order[i]) < channels.indexOf(required_channels_in_order[i+1]))
+ }
+
+ if (channels_missing | channel_priority_violation) {
+ log.warn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ " There is a problem with your Conda configuration!\n\n" +
+ " You will need to set-up the conda-forge and bioconda channels correctly.\n" +
+ " Please refer to https://bioconda.github.io/\n" +
+ " The observed channel order is \n" +
+ " ${channels}\n" +
+ " but the following channel order is required:\n" +
+ " ${required_channels_in_order}\n" +
+ "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ }
+}
diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/meta.yml b/subworkflows/nf-core/utils_nextflow_pipeline/meta.yml
new file mode 100644
index 00000000..e5c3a0a8
--- /dev/null
+++ b/subworkflows/nf-core/utils_nextflow_pipeline/meta.yml
@@ -0,0 +1,38 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json
+name: "UTILS_NEXTFLOW_PIPELINE"
+description: Subworkflow with functionality that may be useful for any Nextflow pipeline
+keywords:
+ - utility
+ - pipeline
+ - initialise
+ - version
+components: []
+input:
+ - print_version:
+ type: boolean
+ description: |
+ Print the version of the pipeline and exit
+ - dump_parameters:
+ type: boolean
+ description: |
+ Dump the parameters of the pipeline to a JSON file
+ - output_directory:
+ type: directory
+ description: Path to output dir to write JSON file to.
+ pattern: "results/"
+ - check_conda_channel:
+ type: boolean
+ description: |
+ Check if the conda channel priority is correct.
+output:
+ - dummy_emit:
+ type: boolean
+ description: |
+ Dummy emit to make nf-core subworkflows lint happy
+authors:
+ - "@adamrtalbot"
+ - "@drpatelh"
+maintainers:
+ - "@adamrtalbot"
+ - "@drpatelh"
+ - "@maxulysse"
diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test
new file mode 100644
index 00000000..8ed4310c
--- /dev/null
+++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test
@@ -0,0 +1,54 @@
+
+nextflow_function {
+
+ name "Test Functions"
+ script "subworkflows/nf-core/utils_nextflow_pipeline/main.nf"
+ config "subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config"
+ tag 'subworkflows'
+ tag 'utils_nextflow_pipeline'
+ tag 'subworkflows/utils_nextflow_pipeline'
+
+ test("Test Function getWorkflowVersion") {
+
+ function "getWorkflowVersion"
+
+ then {
+ assertAll(
+ { assert function.success },
+ { assert snapshot(function.result).match() }
+ )
+ }
+ }
+
+ test("Test Function dumpParametersToJSON") {
+
+ function "dumpParametersToJSON"
+
+ when {
+ function {
+ """
+ // define inputs of the function here. Example:
+ input[0] = "$outputDir"
+ """.stripIndent()
+ }
+ }
+
+ then {
+ assertAll(
+ { assert function.success }
+ )
+ }
+ }
+
+ test("Test Function checkCondaChannels") {
+
+ function "checkCondaChannels"
+
+ then {
+ assertAll(
+ { assert function.success },
+ { assert snapshot(function.result).match() }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test.snap b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test.snap
new file mode 100644
index 00000000..db2030f8
--- /dev/null
+++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test.snap
@@ -0,0 +1,12 @@
+{
+ "Test Function getWorkflowVersion": {
+ "content": [
+ "v9.9.9"
+ ],
+ "timestamp": "2024-01-19T11:32:36.031083"
+ },
+ "Test Function checkCondaChannels": {
+ "content": null,
+ "timestamp": "2024-01-19T11:32:50.456"
+ }
+}
\ No newline at end of file
diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.workflow.nf.test b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.workflow.nf.test
new file mode 100644
index 00000000..f7c54bc6
--- /dev/null
+++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.workflow.nf.test
@@ -0,0 +1,123 @@
+nextflow_workflow {
+
+ name "Test Workflow UTILS_NEXTFLOW_PIPELINE"
+ script "../main.nf"
+ config "subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config"
+ workflow "UTILS_NEXTFLOW_PIPELINE"
+ tag 'subworkflows'
+ tag 'utils_nextflow_pipeline'
+ tag 'subworkflows/utils_nextflow_pipeline'
+
+ test("Should run no inputs") {
+
+ when {
+ params {
+ outdir = "tests/results"
+ }
+ workflow {
+ """
+ print_version = false
+ dump_parameters = false
+ outdir = null
+ check_conda_channels = false
+
+ input[0] = print_version
+ input[1] = dump_parameters
+ input[2] = outdir
+ input[3] = check_conda_channels
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.success }
+ )
+ }
+ }
+
+ test("Should print version") {
+
+ when {
+ params {
+ outdir = "tests/results"
+ }
+ workflow {
+ """
+ print_version = true
+ dump_parameters = false
+ outdir = null
+ check_conda_channels = false
+
+ input[0] = print_version
+ input[1] = dump_parameters
+ input[2] = outdir
+ input[3] = check_conda_channels
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.success },
+ { assert workflow.stdout.contains("nextflow_workflow v9.9.9") }
+ )
+ }
+ }
+
+ test("Should dump params") {
+
+ when {
+ params {
+ outdir = "$outputDir"
+ }
+ workflow {
+ """
+ print_version = false
+ dump_parameters = true
+ outdir = params.outdir
+ check_conda_channels = false
+
+ input[0] = false
+ input[1] = true
+ input[2] = params.outdir
+ input[3] = false
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.success }
+ )
+ }
+ }
+
+ test("Should not create params JSON if no output directory") {
+
+ when {
+ params {
+ outdir = "$outputDir"
+ }
+ workflow {
+ """
+ print_version = false
+ dump_parameters = true
+ outdir = params.outdir
+ check_conda_channels = false
+
+ input[0] = false
+ input[1] = true
+ input[2] = null
+ input[3] = false
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.success }
+ )
+ }
+ }
+}
diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config b/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config
new file mode 100644
index 00000000..53574ffe
--- /dev/null
+++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config
@@ -0,0 +1,9 @@
+manifest {
+ name = 'nextflow_workflow'
+ author = """nf-core"""
+ homePage = 'https://127.0.0.1'
+ description = """Dummy pipeline"""
+ nextflowVersion = '!>=23.04.0'
+ version = '9.9.9'
+ doi = 'https://doi.org/10.5281/zenodo.5070524'
+}
\ No newline at end of file
diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/tags.yml b/subworkflows/nf-core/utils_nextflow_pipeline/tests/tags.yml
new file mode 100644
index 00000000..f8476112
--- /dev/null
+++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/tags.yml
@@ -0,0 +1,2 @@
+subworkflows/utils_nextflow_pipeline:
+ - subworkflows/nf-core/utils_nextflow_pipeline/**
diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/main.nf b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf
new file mode 100644
index 00000000..a8b55d6f
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf
@@ -0,0 +1,440 @@
+//
+// Subworkflow with utility functions specific to the nf-core pipeline template
+//
+
+import org.yaml.snakeyaml.Yaml
+import nextflow.extension.FilesEx
+
+/*
+========================================================================================
+ SUBWORKFLOW DEFINITION
+========================================================================================
+*/
+
+workflow UTILS_NFCORE_PIPELINE {
+
+ take:
+ nextflow_cli_args
+
+ main:
+ valid_config = checkConfigProvided()
+ checkProfileProvided(nextflow_cli_args)
+
+ emit:
+ valid_config
+}
+
+/*
+========================================================================================
+ FUNCTIONS
+========================================================================================
+*/
+
+//
+// Warn if a -profile or Nextflow config has not been provided to run the pipeline
+//
+def checkConfigProvided() {
+ valid_config = true
+ if (workflow.profile == 'standard' && workflow.configFiles.size() <= 1) {
+ log.warn "[$workflow.manifest.name] You are attempting to run the pipeline without any custom configuration!\n\n" +
+ "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" +
+ " (1) Using an existing pipeline profile e.g. `-profile docker` or `-profile singularity`\n" +
+ " (2) Using an existing nf-core/configs for your Institution e.g. `-profile crick` or `-profile uppmax`\n" +
+ " (3) Using your own local custom config e.g. `-c /path/to/your/custom.config`\n\n" +
+ "Please refer to the quick start section and usage docs for the pipeline.\n "
+ valid_config = false
+ }
+ return valid_config
+}
+
+//
+// Exit pipeline if --profile contains spaces
+//
+def checkProfileProvided(nextflow_cli_args) {
+ if (workflow.profile.endsWith(',')) {
+ error "The `-profile` option cannot end with a trailing comma, please remove it and re-run the pipeline!\n" +
+ "HINT: A common mistake is to provide multiple values separated by spaces e.g. `-profile test, docker`.\n"
+ }
+ if (nextflow_cli_args[0]) {
+ log.warn "nf-core pipelines do not accept positional arguments. The positional argument `${nextflow_cli_args[0]}` has been detected.\n" +
+ "HINT: A common mistake is to provide multiple values separated by spaces e.g. `-profile test, docker`.\n"
+ }
+}
+
+//
+// Citation string for pipeline
+//
+def workflowCitation() {
+ return "If you use ${workflow.manifest.name} for your analysis please cite:\n\n" +
+ "* The pipeline\n" +
+ " ${workflow.manifest.doi}\n\n" +
+ "* The nf-core framework\n" +
+ " https://doi.org/10.1038/s41587-020-0439-x\n\n" +
+ "* Software dependencies\n" +
+ " https://github.com/${workflow.manifest.name}/blob/master/CITATIONS.md"
+}
+
+//
+// Generate workflow version string
+//
+def getWorkflowVersion() {
+ String version_string = ""
+ if (workflow.manifest.version) {
+ def prefix_v = workflow.manifest.version[0] != 'v' ? 'v' : ''
+ version_string += "${prefix_v}${workflow.manifest.version}"
+ }
+
+ if (workflow.commitId) {
+ def git_shortsha = workflow.commitId.substring(0, 7)
+ version_string += "-g${git_shortsha}"
+ }
+
+ return version_string
+}
+
+//
+// Get software versions for pipeline
+//
+def processVersionsFromYAML(yaml_file) {
+ Yaml yaml = new Yaml()
+ versions = yaml.load(yaml_file).collectEntries { k, v -> [ k.tokenize(':')[-1], v ] }
+ return yaml.dumpAsMap(versions).trim()
+}
+
+//
+// Get workflow version for pipeline
+//
+def workflowVersionToYAML() {
+ return """
+ Workflow:
+ $workflow.manifest.name: ${getWorkflowVersion()}
+ Nextflow: $workflow.nextflow.version
+ """.stripIndent().trim()
+}
+
+//
+// Get channel of software versions used in pipeline in YAML format
+//
+def softwareVersionsToYAML(ch_versions) {
+ return ch_versions
+ .unique()
+ .map { processVersionsFromYAML(it) }
+ .unique()
+ .mix(Channel.of(workflowVersionToYAML()))
+}
+
+//
+// Get workflow summary for MultiQC
+//
+def paramsSummaryMultiqc(summary_params) {
+ def summary_section = ''
+ for (group in summary_params.keySet()) {
+ def group_params = summary_params.get(group) // This gets the parameters of that particular group
+ if (group_params) {
+ summary_section += " $group
\n"
+ summary_section += " \n"
+ for (param in group_params.keySet()) {
+ summary_section += " - $param
- ${group_params.get(param) ?: 'N/A'}
\n"
+ }
+ summary_section += "
\n"
+ }
+ }
+
+ String yaml_file_text = "id: '${workflow.manifest.name.replace('/','-')}-summary'\n"
+ yaml_file_text += "description: ' - this information is collected when the pipeline is started.'\n"
+ yaml_file_text += "section_name: '${workflow.manifest.name} Workflow Summary'\n"
+ yaml_file_text += "section_href: 'https://github.com/${workflow.manifest.name}'\n"
+ yaml_file_text += "plot_type: 'html'\n"
+ yaml_file_text += "data: |\n"
+ yaml_file_text += "${summary_section}"
+
+ return yaml_file_text
+}
+
+//
+// nf-core logo
+//
+def nfCoreLogo(monochrome_logs=true) {
+ Map colors = logColours(monochrome_logs)
+ String.format(
+ """\n
+ ${dashedLine(monochrome_logs)}
+ ${colors.green},--.${colors.black}/${colors.green},-.${colors.reset}
+ ${colors.blue} ___ __ __ __ ___ ${colors.green}/,-._.--~\'${colors.reset}
+ ${colors.blue} |\\ | |__ __ / ` / \\ |__) |__ ${colors.yellow}} {${colors.reset}
+ ${colors.blue} | \\| | \\__, \\__/ | \\ |___ ${colors.green}\\`-._,-`-,${colors.reset}
+ ${colors.green}`._,._,\'${colors.reset}
+ ${colors.purple} ${workflow.manifest.name} ${getWorkflowVersion()}${colors.reset}
+ ${dashedLine(monochrome_logs)}
+ """.stripIndent()
+ )
+}
+
+//
+// Return dashed line
+//
+def dashedLine(monochrome_logs=true) {
+ Map colors = logColours(monochrome_logs)
+ return "-${colors.dim}----------------------------------------------------${colors.reset}-"
+}
+
+//
+// ANSII colours used for terminal logging
+//
+def logColours(monochrome_logs=true) {
+ Map colorcodes = [:]
+
+ // Reset / Meta
+ colorcodes['reset'] = monochrome_logs ? '' : "\033[0m"
+ colorcodes['bold'] = monochrome_logs ? '' : "\033[1m"
+ colorcodes['dim'] = monochrome_logs ? '' : "\033[2m"
+ colorcodes['underlined'] = monochrome_logs ? '' : "\033[4m"
+ colorcodes['blink'] = monochrome_logs ? '' : "\033[5m"
+ colorcodes['reverse'] = monochrome_logs ? '' : "\033[7m"
+ colorcodes['hidden'] = monochrome_logs ? '' : "\033[8m"
+
+ // Regular Colors
+ colorcodes['black'] = monochrome_logs ? '' : "\033[0;30m"
+ colorcodes['red'] = monochrome_logs ? '' : "\033[0;31m"
+ colorcodes['green'] = monochrome_logs ? '' : "\033[0;32m"
+ colorcodes['yellow'] = monochrome_logs ? '' : "\033[0;33m"
+ colorcodes['blue'] = monochrome_logs ? '' : "\033[0;34m"
+ colorcodes['purple'] = monochrome_logs ? '' : "\033[0;35m"
+ colorcodes['cyan'] = monochrome_logs ? '' : "\033[0;36m"
+ colorcodes['white'] = monochrome_logs ? '' : "\033[0;37m"
+
+ // Bold
+ colorcodes['bblack'] = monochrome_logs ? '' : "\033[1;30m"
+ colorcodes['bred'] = monochrome_logs ? '' : "\033[1;31m"
+ colorcodes['bgreen'] = monochrome_logs ? '' : "\033[1;32m"
+ colorcodes['byellow'] = monochrome_logs ? '' : "\033[1;33m"
+ colorcodes['bblue'] = monochrome_logs ? '' : "\033[1;34m"
+ colorcodes['bpurple'] = monochrome_logs ? '' : "\033[1;35m"
+ colorcodes['bcyan'] = monochrome_logs ? '' : "\033[1;36m"
+ colorcodes['bwhite'] = monochrome_logs ? '' : "\033[1;37m"
+
+ // Underline
+ colorcodes['ublack'] = monochrome_logs ? '' : "\033[4;30m"
+ colorcodes['ured'] = monochrome_logs ? '' : "\033[4;31m"
+ colorcodes['ugreen'] = monochrome_logs ? '' : "\033[4;32m"
+ colorcodes['uyellow'] = monochrome_logs ? '' : "\033[4;33m"
+ colorcodes['ublue'] = monochrome_logs ? '' : "\033[4;34m"
+ colorcodes['upurple'] = monochrome_logs ? '' : "\033[4;35m"
+ colorcodes['ucyan'] = monochrome_logs ? '' : "\033[4;36m"
+ colorcodes['uwhite'] = monochrome_logs ? '' : "\033[4;37m"
+
+ // High Intensity
+ colorcodes['iblack'] = monochrome_logs ? '' : "\033[0;90m"
+ colorcodes['ired'] = monochrome_logs ? '' : "\033[0;91m"
+ colorcodes['igreen'] = monochrome_logs ? '' : "\033[0;92m"
+ colorcodes['iyellow'] = monochrome_logs ? '' : "\033[0;93m"
+ colorcodes['iblue'] = monochrome_logs ? '' : "\033[0;94m"
+ colorcodes['ipurple'] = monochrome_logs ? '' : "\033[0;95m"
+ colorcodes['icyan'] = monochrome_logs ? '' : "\033[0;96m"
+ colorcodes['iwhite'] = monochrome_logs ? '' : "\033[0;97m"
+
+ // Bold High Intensity
+ colorcodes['biblack'] = monochrome_logs ? '' : "\033[1;90m"
+ colorcodes['bired'] = monochrome_logs ? '' : "\033[1;91m"
+ colorcodes['bigreen'] = monochrome_logs ? '' : "\033[1;92m"
+ colorcodes['biyellow'] = monochrome_logs ? '' : "\033[1;93m"
+ colorcodes['biblue'] = monochrome_logs ? '' : "\033[1;94m"
+ colorcodes['bipurple'] = monochrome_logs ? '' : "\033[1;95m"
+ colorcodes['bicyan'] = monochrome_logs ? '' : "\033[1;96m"
+ colorcodes['biwhite'] = monochrome_logs ? '' : "\033[1;97m"
+
+ return colorcodes
+}
+
+//
+// Attach the multiqc report to email
+//
+def attachMultiqcReport(multiqc_report) {
+ def mqc_report = null
+ try {
+ if (workflow.success) {
+ mqc_report = multiqc_report.getVal()
+ if (mqc_report.getClass() == ArrayList && mqc_report.size() >= 1) {
+ if (mqc_report.size() > 1) {
+ log.warn "[$workflow.manifest.name] Found multiple reports from process 'MULTIQC', will use only one"
+ }
+ mqc_report = mqc_report[0]
+ }
+ }
+ } catch (all) {
+ if (multiqc_report) {
+ log.warn "[$workflow.manifest.name] Could not attach MultiQC report to summary email"
+ }
+ }
+ return mqc_report
+}
+
+//
+// Construct and send completion email
+//
+def completionEmail(summary_params, email, email_on_fail, plaintext_email, outdir, monochrome_logs=true, multiqc_report=null) {
+
+ // Set up the e-mail variables
+ def subject = "[$workflow.manifest.name] Successful: $workflow.runName"
+ if (!workflow.success) {
+ subject = "[$workflow.manifest.name] FAILED: $workflow.runName"
+ }
+
+ def summary = [:]
+ for (group in summary_params.keySet()) {
+ summary << summary_params[group]
+ }
+
+ def misc_fields = [:]
+ misc_fields['Date Started'] = workflow.start
+ misc_fields['Date Completed'] = workflow.complete
+ misc_fields['Pipeline script file path'] = workflow.scriptFile
+ misc_fields['Pipeline script hash ID'] = workflow.scriptId
+ if (workflow.repository) misc_fields['Pipeline repository Git URL'] = workflow.repository
+ if (workflow.commitId) misc_fields['Pipeline repository Git Commit'] = workflow.commitId
+ if (workflow.revision) misc_fields['Pipeline Git branch/tag'] = workflow.revision
+ misc_fields['Nextflow Version'] = workflow.nextflow.version
+ misc_fields['Nextflow Build'] = workflow.nextflow.build
+ misc_fields['Nextflow Compile Timestamp'] = workflow.nextflow.timestamp
+
+ def email_fields = [:]
+ email_fields['version'] = getWorkflowVersion()
+ email_fields['runName'] = workflow.runName
+ email_fields['success'] = workflow.success
+ email_fields['dateComplete'] = workflow.complete
+ email_fields['duration'] = workflow.duration
+ email_fields['exitStatus'] = workflow.exitStatus
+ email_fields['errorMessage'] = (workflow.errorMessage ?: 'None')
+ email_fields['errorReport'] = (workflow.errorReport ?: 'None')
+ email_fields['commandLine'] = workflow.commandLine
+ email_fields['projectDir'] = workflow.projectDir
+ email_fields['summary'] = summary << misc_fields
+
+ // On success try attach the multiqc report
+ def mqc_report = attachMultiqcReport(multiqc_report)
+
+ // Check if we are only sending emails on failure
+ def email_address = email
+ if (!email && email_on_fail && !workflow.success) {
+ email_address = email_on_fail
+ }
+
+ // Render the TXT template
+ def engine = new groovy.text.GStringTemplateEngine()
+ def tf = new File("${workflow.projectDir}/assets/email_template.txt")
+ def txt_template = engine.createTemplate(tf).make(email_fields)
+ def email_txt = txt_template.toString()
+
+ // Render the HTML template
+ def hf = new File("${workflow.projectDir}/assets/email_template.html")
+ def html_template = engine.createTemplate(hf).make(email_fields)
+ def email_html = html_template.toString()
+
+ // Render the sendmail template
+ def max_multiqc_email_size = (params.containsKey('max_multiqc_email_size') ? params.max_multiqc_email_size : 0) as nextflow.util.MemoryUnit
+ def smail_fields = [ email: email_address, subject: subject, email_txt: email_txt, email_html: email_html, projectDir: "${workflow.projectDir}", mqcFile: mqc_report, mqcMaxSize: max_multiqc_email_size.toBytes() ]
+ def sf = new File("${workflow.projectDir}/assets/sendmail_template.txt")
+ def sendmail_template = engine.createTemplate(sf).make(smail_fields)
+ def sendmail_html = sendmail_template.toString()
+
+ // Send the HTML e-mail
+ Map colors = logColours(monochrome_logs)
+ if (email_address) {
+ try {
+ if (plaintext_email) { throw GroovyException('Send plaintext e-mail, not HTML') }
+ // Try to send HTML e-mail using sendmail
+ def sendmail_tf = new File(workflow.launchDir.toString(), ".sendmail_tmp.html")
+ sendmail_tf.withWriter { w -> w << sendmail_html }
+ [ 'sendmail', '-t' ].execute() << sendmail_html
+ log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (sendmail)-"
+ } catch (all) {
+ // Catch failures and try with plaintext
+ def mail_cmd = [ 'mail', '-s', subject, '--content-type=text/html', email_address ]
+ mail_cmd.execute() << email_html
+ log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (mail)-"
+ }
+ }
+
+ // Write summary e-mail HTML to a file
+ def output_hf = new File(workflow.launchDir.toString(), ".pipeline_report.html")
+ output_hf.withWriter { w -> w << email_html }
+ FilesEx.copyTo(output_hf.toPath(), "${outdir}/pipeline_info/pipeline_report.html");
+ output_hf.delete()
+
+ // Write summary e-mail TXT to a file
+ def output_tf = new File(workflow.launchDir.toString(), ".pipeline_report.txt")
+ output_tf.withWriter { w -> w << email_txt }
+ FilesEx.copyTo(output_tf.toPath(), "${outdir}/pipeline_info/pipeline_report.txt");
+ output_tf.delete()
+}
+
+//
+// Print pipeline summary on completion
+//
+def completionSummary(monochrome_logs=true) {
+ Map colors = logColours(monochrome_logs)
+ if (workflow.success) {
+ if (workflow.stats.ignoredCount == 0) {
+ log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Pipeline completed successfully${colors.reset}-"
+ } else {
+ log.info "-${colors.purple}[$workflow.manifest.name]${colors.yellow} Pipeline completed successfully, but with errored process(es) ${colors.reset}-"
+ }
+ } else {
+ log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-"
+ }
+}
+
+//
+// Construct and send a notification to a web server as JSON e.g. Microsoft Teams and Slack
+//
+def imNotification(summary_params, hook_url) {
+ def summary = [:]
+ for (group in summary_params.keySet()) {
+ summary << summary_params[group]
+ }
+
+ def misc_fields = [:]
+ misc_fields['start'] = workflow.start
+ misc_fields['complete'] = workflow.complete
+ misc_fields['scriptfile'] = workflow.scriptFile
+ misc_fields['scriptid'] = workflow.scriptId
+ if (workflow.repository) misc_fields['repository'] = workflow.repository
+ if (workflow.commitId) misc_fields['commitid'] = workflow.commitId
+ if (workflow.revision) misc_fields['revision'] = workflow.revision
+ misc_fields['nxf_version'] = workflow.nextflow.version
+ misc_fields['nxf_build'] = workflow.nextflow.build
+ misc_fields['nxf_timestamp'] = workflow.nextflow.timestamp
+
+ def msg_fields = [:]
+ msg_fields['version'] = getWorkflowVersion()
+ msg_fields['runName'] = workflow.runName
+ msg_fields['success'] = workflow.success
+ msg_fields['dateComplete'] = workflow.complete
+ msg_fields['duration'] = workflow.duration
+ msg_fields['exitStatus'] = workflow.exitStatus
+ msg_fields['errorMessage'] = (workflow.errorMessage ?: 'None')
+ msg_fields['errorReport'] = (workflow.errorReport ?: 'None')
+ msg_fields['commandLine'] = workflow.commandLine.replaceFirst(/ +--hook_url +[^ ]+/, "")
+ msg_fields['projectDir'] = workflow.projectDir
+ msg_fields['summary'] = summary << misc_fields
+
+ // Render the JSON template
+ def engine = new groovy.text.GStringTemplateEngine()
+ // Different JSON depending on the service provider
+ // Defaults to "Adaptive Cards" (https://adaptivecards.io), except Slack which has its own format
+ def json_path = hook_url.contains("hooks.slack.com") ? "slackreport.json" : "adaptivecard.json"
+ def hf = new File("${workflow.projectDir}/assets/${json_path}")
+ def json_template = engine.createTemplate(hf).make(msg_fields)
+ def json_message = json_template.toString()
+
+ // POST
+ def post = new URL(hook_url).openConnection();
+ post.setRequestMethod("POST")
+ post.setDoOutput(true)
+ post.setRequestProperty("Content-Type", "application/json")
+ post.getOutputStream().write(json_message.getBytes("UTF-8"));
+ def postRC = post.getResponseCode();
+ if (! postRC.equals(200)) {
+ log.warn(post.getErrorStream().getText());
+ }
+}
diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/meta.yml b/subworkflows/nf-core/utils_nfcore_pipeline/meta.yml
new file mode 100644
index 00000000..d08d2434
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfcore_pipeline/meta.yml
@@ -0,0 +1,24 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json
+name: "UTILS_NFCORE_PIPELINE"
+description: Subworkflow with utility functions specific to the nf-core pipeline template
+keywords:
+ - utility
+ - pipeline
+ - initialise
+ - version
+components: []
+input:
+ - nextflow_cli_args:
+ type: list
+ description: |
+ Nextflow CLI positional arguments
+output:
+ - success:
+ type: boolean
+ description: |
+ Dummy output to indicate success
+authors:
+ - "@adamrtalbot"
+maintainers:
+ - "@adamrtalbot"
+ - "@maxulysse"
diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test
new file mode 100644
index 00000000..1dc317f8
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test
@@ -0,0 +1,134 @@
+
+nextflow_function {
+
+ name "Test Functions"
+ script "../main.nf"
+ config "subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config"
+ tag "subworkflows"
+ tag "subworkflows_nfcore"
+ tag "utils_nfcore_pipeline"
+ tag "subworkflows/utils_nfcore_pipeline"
+
+ test("Test Function checkConfigProvided") {
+
+ function "checkConfigProvided"
+
+ then {
+ assertAll(
+ { assert function.success },
+ { assert snapshot(function.result).match() }
+ )
+ }
+ }
+
+ test("Test Function checkProfileProvided") {
+
+ function "checkProfileProvided"
+
+ when {
+ function {
+ """
+ input[0] = []
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert function.success },
+ { assert snapshot(function.result).match() }
+ )
+ }
+ }
+
+ test("Test Function workflowCitation") {
+
+ function "workflowCitation"
+
+ then {
+ assertAll(
+ { assert function.success },
+ { assert snapshot(function.result).match() }
+ )
+ }
+ }
+
+ test("Test Function nfCoreLogo") {
+
+ function "nfCoreLogo"
+
+ when {
+ function {
+ """
+ input[0] = false
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert function.success },
+ { assert snapshot(function.result).match() }
+ )
+ }
+ }
+
+ test("Test Function dashedLine") {
+
+ function "dashedLine"
+
+ when {
+ function {
+ """
+ input[0] = false
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert function.success },
+ { assert snapshot(function.result).match() }
+ )
+ }
+ }
+
+ test("Test Function without logColours") {
+
+ function "logColours"
+
+ when {
+ function {
+ """
+ input[0] = true
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert function.success },
+ { assert snapshot(function.result).match() }
+ )
+ }
+ }
+
+ test("Test Function with logColours") {
+ function "logColours"
+
+ when {
+ function {
+ """
+ input[0] = false
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert function.success },
+ { assert snapshot(function.result).match() }
+ )
+ }
+ }
+}
diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test.snap b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test.snap
new file mode 100644
index 00000000..10f948e6
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test.snap
@@ -0,0 +1,138 @@
+{
+ "Test Function checkProfileProvided": {
+ "content": null,
+ "timestamp": "2024-02-09T15:43:55.145717"
+ },
+ "Test Function checkConfigProvided": {
+ "content": [
+ true
+ ],
+ "timestamp": "2024-01-19T11:34:13.548431224"
+ },
+ "Test Function nfCoreLogo": {
+ "content": [
+ "\n\n-\u001b[2m----------------------------------------------------\u001b[0m-\n \u001b[0;32m,--.\u001b[0;30m/\u001b[0;32m,-.\u001b[0m\n\u001b[0;34m ___ __ __ __ ___ \u001b[0;32m/,-._.--~'\u001b[0m\n\u001b[0;34m |\\ | |__ __ / ` / \\ |__) |__ \u001b[0;33m} {\u001b[0m\n\u001b[0;34m | \\| | \\__, \\__/ | \\ |___ \u001b[0;32m\\`-._,-`-,\u001b[0m\n \u001b[0;32m`._,._,'\u001b[0m\n\u001b[0;35m nextflow_workflow v9.9.9\u001b[0m\n-\u001b[2m----------------------------------------------------\u001b[0m-\n"
+ ],
+ "timestamp": "2024-01-19T11:34:38.840454873"
+ },
+ "Test Function workflowCitation": {
+ "content": [
+ "If you use nextflow_workflow for your analysis please cite:\n\n* The pipeline\n https://doi.org/10.5281/zenodo.5070524\n\n* The nf-core framework\n https://doi.org/10.1038/s41587-020-0439-x\n\n* Software dependencies\n https://github.com/nextflow_workflow/blob/master/CITATIONS.md"
+ ],
+ "timestamp": "2024-01-19T11:34:22.24352016"
+ },
+ "Test Function without logColours": {
+ "content": [
+ {
+ "reset": "",
+ "bold": "",
+ "dim": "",
+ "underlined": "",
+ "blink": "",
+ "reverse": "",
+ "hidden": "",
+ "black": "",
+ "red": "",
+ "green": "",
+ "yellow": "",
+ "blue": "",
+ "purple": "",
+ "cyan": "",
+ "white": "",
+ "bblack": "",
+ "bred": "",
+ "bgreen": "",
+ "byellow": "",
+ "bblue": "",
+ "bpurple": "",
+ "bcyan": "",
+ "bwhite": "",
+ "ublack": "",
+ "ured": "",
+ "ugreen": "",
+ "uyellow": "",
+ "ublue": "",
+ "upurple": "",
+ "ucyan": "",
+ "uwhite": "",
+ "iblack": "",
+ "ired": "",
+ "igreen": "",
+ "iyellow": "",
+ "iblue": "",
+ "ipurple": "",
+ "icyan": "",
+ "iwhite": "",
+ "biblack": "",
+ "bired": "",
+ "bigreen": "",
+ "biyellow": "",
+ "biblue": "",
+ "bipurple": "",
+ "bicyan": "",
+ "biwhite": ""
+ }
+ ],
+ "timestamp": "2024-01-19T11:35:04.418416984"
+ },
+ "Test Function dashedLine": {
+ "content": [
+ "-\u001b[2m----------------------------------------------------\u001b[0m-"
+ ],
+ "timestamp": "2024-01-19T11:34:55.420000755"
+ },
+ "Test Function with logColours": {
+ "content": [
+ {
+ "reset": "\u001b[0m",
+ "bold": "\u001b[1m",
+ "dim": "\u001b[2m",
+ "underlined": "\u001b[4m",
+ "blink": "\u001b[5m",
+ "reverse": "\u001b[7m",
+ "hidden": "\u001b[8m",
+ "black": "\u001b[0;30m",
+ "red": "\u001b[0;31m",
+ "green": "\u001b[0;32m",
+ "yellow": "\u001b[0;33m",
+ "blue": "\u001b[0;34m",
+ "purple": "\u001b[0;35m",
+ "cyan": "\u001b[0;36m",
+ "white": "\u001b[0;37m",
+ "bblack": "\u001b[1;30m",
+ "bred": "\u001b[1;31m",
+ "bgreen": "\u001b[1;32m",
+ "byellow": "\u001b[1;33m",
+ "bblue": "\u001b[1;34m",
+ "bpurple": "\u001b[1;35m",
+ "bcyan": "\u001b[1;36m",
+ "bwhite": "\u001b[1;37m",
+ "ublack": "\u001b[4;30m",
+ "ured": "\u001b[4;31m",
+ "ugreen": "\u001b[4;32m",
+ "uyellow": "\u001b[4;33m",
+ "ublue": "\u001b[4;34m",
+ "upurple": "\u001b[4;35m",
+ "ucyan": "\u001b[4;36m",
+ "uwhite": "\u001b[4;37m",
+ "iblack": "\u001b[0;90m",
+ "ired": "\u001b[0;91m",
+ "igreen": "\u001b[0;92m",
+ "iyellow": "\u001b[0;93m",
+ "iblue": "\u001b[0;94m",
+ "ipurple": "\u001b[0;95m",
+ "icyan": "\u001b[0;96m",
+ "iwhite": "\u001b[0;97m",
+ "biblack": "\u001b[1;90m",
+ "bired": "\u001b[1;91m",
+ "bigreen": "\u001b[1;92m",
+ "biyellow": "\u001b[1;93m",
+ "biblue": "\u001b[1;94m",
+ "bipurple": "\u001b[1;95m",
+ "bicyan": "\u001b[1;96m",
+ "biwhite": "\u001b[1;97m"
+ }
+ ],
+ "timestamp": "2024-01-19T11:35:13.436366565"
+ }
+}
\ No newline at end of file
diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test
new file mode 100644
index 00000000..8940d32d
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test
@@ -0,0 +1,29 @@
+nextflow_workflow {
+
+ name "Test Workflow UTILS_NFCORE_PIPELINE"
+ script "../main.nf"
+ config "subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config"
+ workflow "UTILS_NFCORE_PIPELINE"
+ tag "subworkflows"
+ tag "subworkflows_nfcore"
+ tag "utils_nfcore_pipeline"
+ tag "subworkflows/utils_nfcore_pipeline"
+
+ test("Should run without failures") {
+
+ when {
+ workflow {
+ """
+ input[0] = []
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.success },
+ { assert snapshot(workflow.out).match() }
+ )
+ }
+ }
+}
diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap
new file mode 100644
index 00000000..d07ce54c
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap
@@ -0,0 +1,15 @@
+{
+ "Should run without failures": {
+ "content": [
+ {
+ "0": [
+ true
+ ],
+ "valid_config": [
+ true
+ ]
+ }
+ ],
+ "timestamp": "2024-01-19T11:35:22.538940073"
+ }
+}
\ No newline at end of file
diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config b/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config
new file mode 100644
index 00000000..d0a926bf
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config
@@ -0,0 +1,9 @@
+manifest {
+ name = 'nextflow_workflow'
+ author = """nf-core"""
+ homePage = 'https://127.0.0.1'
+ description = """Dummy pipeline"""
+ nextflowVersion = '!>=23.04.0'
+ version = '9.9.9'
+ doi = 'https://doi.org/10.5281/zenodo.5070524'
+}
diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/tags.yml b/subworkflows/nf-core/utils_nfcore_pipeline/tests/tags.yml
new file mode 100644
index 00000000..ac8523c9
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfcore_pipeline/tests/tags.yml
@@ -0,0 +1,2 @@
+subworkflows/utils_nfcore_pipeline:
+ - subworkflows/nf-core/utils_nfcore_pipeline/**
diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/main.nf b/subworkflows/nf-core/utils_nfvalidation_plugin/main.nf
new file mode 100644
index 00000000..2585b65d
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfvalidation_plugin/main.nf
@@ -0,0 +1,62 @@
+//
+// Subworkflow that uses the nf-validation plugin to render help text and parameter summary
+//
+
+/*
+========================================================================================
+ IMPORT NF-VALIDATION PLUGIN
+========================================================================================
+*/
+
+include { paramsHelp } from 'plugin/nf-validation'
+include { paramsSummaryLog } from 'plugin/nf-validation'
+include { validateParameters } from 'plugin/nf-validation'
+
+/*
+========================================================================================
+ SUBWORKFLOW DEFINITION
+========================================================================================
+*/
+
+workflow UTILS_NFVALIDATION_PLUGIN {
+
+ take:
+ print_help // boolean: print help
+ workflow_command // string: default commmand used to run pipeline
+ pre_help_text // string: string to be printed before help text and summary log
+ post_help_text // string: string to be printed after help text and summary log
+ validate_params // boolean: validate parameters
+ schema_filename // path: JSON schema file, null to use default value
+
+ main:
+
+ log.debug "Using schema file: ${schema_filename}"
+
+ // Default values for strings
+ pre_help_text = pre_help_text ?: ''
+ post_help_text = post_help_text ?: ''
+ workflow_command = workflow_command ?: ''
+
+ //
+ // Print help message if needed
+ //
+ if (print_help) {
+ log.info pre_help_text + paramsHelp(workflow_command, parameters_schema: schema_filename) + post_help_text
+ System.exit(0)
+ }
+
+ //
+ // Print parameter summary to stdout
+ //
+ log.info pre_help_text + paramsSummaryLog(workflow, parameters_schema: schema_filename) + post_help_text
+
+ //
+ // Validate parameters relative to the parameter JSON schema
+ //
+ if (validate_params){
+ validateParameters(parameters_schema: schema_filename)
+ }
+
+ emit:
+ dummy_emit = true
+}
diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/meta.yml b/subworkflows/nf-core/utils_nfvalidation_plugin/meta.yml
new file mode 100644
index 00000000..3d4a6b04
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfvalidation_plugin/meta.yml
@@ -0,0 +1,44 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json
+name: "UTILS_NFVALIDATION_PLUGIN"
+description: Use nf-validation to initiate and validate a pipeline
+keywords:
+ - utility
+ - pipeline
+ - initialise
+ - validation
+components: []
+input:
+ - print_help:
+ type: boolean
+ description: |
+ Print help message and exit
+ - workflow_command:
+ type: string
+ description: |
+ The command to run the workflow e.g. "nextflow run main.nf"
+ - pre_help_text:
+ type: string
+ description: |
+ Text to print before the help message
+ - post_help_text:
+ type: string
+ description: |
+ Text to print after the help message
+ - validate_params:
+ type: boolean
+ description: |
+ Validate the parameters and error if invalid.
+ - schema_filename:
+ type: string
+ description: |
+ The filename of the schema to validate against.
+output:
+ - dummy_emit:
+ type: boolean
+ description: |
+ Dummy emit to make nf-core subworkflows lint happy
+authors:
+ - "@adamrtalbot"
+maintainers:
+ - "@adamrtalbot"
+ - "@maxulysse"
diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test
new file mode 100644
index 00000000..517ee54e
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test
@@ -0,0 +1,200 @@
+nextflow_workflow {
+
+ name "Test Workflow UTILS_NFVALIDATION_PLUGIN"
+ script "../main.nf"
+ workflow "UTILS_NFVALIDATION_PLUGIN"
+ tag "subworkflows"
+ tag "subworkflows_nfcore"
+ tag "plugin/nf-validation"
+ tag "'plugin/nf-validation'"
+ tag "utils_nfvalidation_plugin"
+ tag "subworkflows/utils_nfvalidation_plugin"
+
+ test("Should run nothing") {
+
+ when {
+
+ params {
+ monochrome_logs = true
+ test_data = ''
+ }
+
+ workflow {
+ """
+ help = false
+ workflow_command = null
+ pre_help_text = null
+ post_help_text = null
+ validate_params = false
+ schema_filename = "$moduleTestDir/nextflow_schema.json"
+
+ input[0] = help
+ input[1] = workflow_command
+ input[2] = pre_help_text
+ input[3] = post_help_text
+ input[4] = validate_params
+ input[5] = schema_filename
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.success }
+ )
+ }
+ }
+
+ test("Should run help") {
+
+
+ when {
+
+ params {
+ monochrome_logs = true
+ test_data = ''
+ }
+ workflow {
+ """
+ help = true
+ workflow_command = null
+ pre_help_text = null
+ post_help_text = null
+ validate_params = false
+ schema_filename = "$moduleTestDir/nextflow_schema.json"
+
+ input[0] = help
+ input[1] = workflow_command
+ input[2] = pre_help_text
+ input[3] = post_help_text
+ input[4] = validate_params
+ input[5] = schema_filename
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.success },
+ { assert workflow.exitStatus == 0 },
+ { assert workflow.stdout.any { it.contains('Input/output options') } },
+ { assert workflow.stdout.any { it.contains('--outdir') } }
+ )
+ }
+ }
+
+ test("Should run help with command") {
+
+ when {
+
+ params {
+ monochrome_logs = true
+ test_data = ''
+ }
+ workflow {
+ """
+ help = true
+ workflow_command = "nextflow run noorg/doesntexist"
+ pre_help_text = null
+ post_help_text = null
+ validate_params = false
+ schema_filename = "$moduleTestDir/nextflow_schema.json"
+
+ input[0] = help
+ input[1] = workflow_command
+ input[2] = pre_help_text
+ input[3] = post_help_text
+ input[4] = validate_params
+ input[5] = schema_filename
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.success },
+ { assert workflow.exitStatus == 0 },
+ { assert workflow.stdout.any { it.contains('nextflow run noorg/doesntexist') } },
+ { assert workflow.stdout.any { it.contains('Input/output options') } },
+ { assert workflow.stdout.any { it.contains('--outdir') } }
+ )
+ }
+ }
+
+ test("Should run help with extra text") {
+
+
+ when {
+
+ params {
+ monochrome_logs = true
+ test_data = ''
+ }
+ workflow {
+ """
+ help = true
+ workflow_command = "nextflow run noorg/doesntexist"
+ pre_help_text = "pre-help-text"
+ post_help_text = "post-help-text"
+ validate_params = false
+ schema_filename = "$moduleTestDir/nextflow_schema.json"
+
+ input[0] = help
+ input[1] = workflow_command
+ input[2] = pre_help_text
+ input[3] = post_help_text
+ input[4] = validate_params
+ input[5] = schema_filename
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.success },
+ { assert workflow.exitStatus == 0 },
+ { assert workflow.stdout.any { it.contains('pre-help-text') } },
+ { assert workflow.stdout.any { it.contains('nextflow run noorg/doesntexist') } },
+ { assert workflow.stdout.any { it.contains('Input/output options') } },
+ { assert workflow.stdout.any { it.contains('--outdir') } },
+ { assert workflow.stdout.any { it.contains('post-help-text') } }
+ )
+ }
+ }
+
+ test("Should validate params") {
+
+ when {
+
+ params {
+ monochrome_logs = true
+ test_data = ''
+ outdir = 1
+ }
+ workflow {
+ """
+ help = false
+ workflow_command = null
+ pre_help_text = null
+ post_help_text = null
+ validate_params = true
+ schema_filename = "$moduleTestDir/nextflow_schema.json"
+
+ input[0] = help
+ input[1] = workflow_command
+ input[2] = pre_help_text
+ input[3] = post_help_text
+ input[4] = validate_params
+ input[5] = schema_filename
+ """
+ }
+ }
+
+ then {
+ assertAll(
+ { assert workflow.failed },
+ { assert workflow.stdout.any { it.contains('ERROR ~ ERROR: Validation of pipeline parameters failed!') } }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/nextflow_schema.json b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/nextflow_schema.json
new file mode 100644
index 00000000..7626c1c9
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/nextflow_schema.json
@@ -0,0 +1,96 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema",
+ "$id": "https://raw.githubusercontent.com/./master/nextflow_schema.json",
+ "title": ". pipeline parameters",
+ "description": "",
+ "type": "object",
+ "definitions": {
+ "input_output_options": {
+ "title": "Input/output options",
+ "type": "object",
+ "fa_icon": "fas fa-terminal",
+ "description": "Define where the pipeline should find input data and save output data.",
+ "required": ["outdir"],
+ "properties": {
+ "validate_params": {
+ "type": "boolean",
+ "description": "Validate parameters?",
+ "default": true,
+ "hidden": true
+ },
+ "outdir": {
+ "type": "string",
+ "format": "directory-path",
+ "description": "The output directory where the results will be saved. You have to use absolute paths to storage on Cloud infrastructure.",
+ "fa_icon": "fas fa-folder-open"
+ },
+ "test_data_base": {
+ "type": "string",
+ "default": "https://raw.githubusercontent.com/nf-core/test-datasets/modules",
+ "description": "Base for test data directory",
+ "hidden": true
+ },
+ "test_data": {
+ "type": "string",
+ "description": "Fake test data param",
+ "hidden": true
+ }
+ }
+ },
+ "generic_options": {
+ "title": "Generic options",
+ "type": "object",
+ "fa_icon": "fas fa-file-import",
+ "description": "Less common options for the pipeline, typically set in a config file.",
+ "help_text": "These options are common to all nf-core pipelines and allow you to customise some of the core preferences for how the pipeline runs.\n\nTypically these options would be set in a Nextflow config file loaded for all pipeline runs, such as `~/.nextflow/config`.",
+ "properties": {
+ "help": {
+ "type": "boolean",
+ "description": "Display help text.",
+ "fa_icon": "fas fa-question-circle",
+ "hidden": true
+ },
+ "version": {
+ "type": "boolean",
+ "description": "Display version and exit.",
+ "fa_icon": "fas fa-question-circle",
+ "hidden": true
+ },
+ "logo": {
+ "type": "boolean",
+ "default": true,
+ "description": "Display nf-core logo in console output.",
+ "fa_icon": "fas fa-image",
+ "hidden": true
+ },
+ "singularity_pull_docker_container": {
+ "type": "boolean",
+ "description": "Pull Singularity container from Docker?",
+ "hidden": true
+ },
+ "publish_dir_mode": {
+ "type": "string",
+ "default": "copy",
+ "description": "Method used to save pipeline results to output directory.",
+ "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.",
+ "fa_icon": "fas fa-copy",
+ "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"],
+ "hidden": true
+ },
+ "monochrome_logs": {
+ "type": "boolean",
+ "description": "Use monochrome_logs",
+ "hidden": true
+ }
+ }
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "#/definitions/input_output_options"
+ },
+ {
+ "$ref": "#/definitions/generic_options"
+ }
+ ]
+}
diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/tags.yml b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/tags.yml
new file mode 100644
index 00000000..60b1cfff
--- /dev/null
+++ b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/tags.yml
@@ -0,0 +1,2 @@
+subworkflows/utils_nfvalidation_plugin:
+ - subworkflows/nf-core/utils_nfvalidation_plugin/**
diff --git a/workflows/mhcquant.nf b/workflows/mhcquant.nf
index 9f5eb975..4dd7417c 100644
--- a/workflows/mhcquant.nf
+++ b/workflows/mhcquant.nf
@@ -1,64 +1,6 @@
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- PRINT PARAMS SUMMARY
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-*/
-
-include { paramsSummaryLog; paramsSummaryMap } from 'plugin/nf-validation'
-
-def logo = NfcoreTemplate.logo(workflow, params.monochrome_logs)
-def citation = '\n' + WorkflowMain.citation(workflow) + '\n'
-def summary_params = paramsSummaryMap(workflow)
-
-// Print parameter summary log to screen
-log.info logo + paramsSummaryLog(workflow) + citation
-
-WorkflowMhcquant.initialise(params, log)
-
-// Input/output options
-if (params.input) { sample_sheet = file(params.input) }
-if (params.fasta) { params.fasta = params.fasta }
-
-// MHC affinity prediction
-if (params.predict_class_1 || params.predict_class_2) {
- Channel.from(file(params.allele_sheet, checkIfExists: true))
- .splitCsv(header: true, sep:'\t')
- .multiMap { col ->
- classI: ["${col.Sample}", "${col.HLA_Alleles_Class_1}"]
- classII: ["${col.Sample}", "${col.HLA_Alleles_Class_2}"] }
- .set { ch_alleles_from_sheet }
-
- // Allele class 1
- if (params.predict_class_1) {
- ch_alleles_from_sheet.classI
- .ifEmpty { exit 1, "params.allele_sheet was empty - no allele input file supplied" }
- .flatMap { it -> [tuple(it[0].toString(), it[1])] }
- .set { peptides_class_1_alleles }
- }
-
- // Allele class 2
- if (params.predict_class_2) {
- ch_alleles_from_sheet.classII
- .ifEmpty { exit 1, "params.allele_sheet was empty - no allele input file supplied" }
- .flatMap { it -> [tuple(it[0].toString(), it[1])] }
- .set { peptides_class_2_alleles }
- }
-}
-
-/*
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- CONFIG FILES
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-*/
-
-ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true)
-ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config, checkIfExists: true) : Channel.empty()
-ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath(params.multiqc_logo, checkIfExists: true) : Channel.empty()
-ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true)
-
-/*
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- CREATE CHANNELS
+ IMPORT LOCAL MODULES/SUBWORKFLOWS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
@@ -70,19 +12,15 @@ include { OPENMS_FILEFILTER } from '../modules/local/openms_filefilter'
include { OPENMS_COMETADAPTER } from '../modules/local/openms_cometadapter'
include { OPENMS_PEPTIDEINDEXER } from '../modules/local/openms_peptideindexer'
include { MS2RESCORE } from '../modules/local/ms2rescore'
-
include { OPENMS_PSMFEATUREEXTRACTOR } from '../modules/local/openms_psmfeatureextractor'
include { OPENMS_PERCOLATORADAPTER } from '../modules/local/openms_percolatoradapter'
include { PYOPENMS_IONANNOTATOR } from '../modules/local/pyopenms_ionannotator'
-
include { OPENMS_TEXTEXPORTER } from '../modules/local/openms_textexporter'
include { OPENMS_MZTABEXPORTER } from '../modules/local/openms_mztabexporter'
-
//
// SUBWORKFLOW: Loaded from subworkflows/local/
//
-include { INPUT_CHECK } from '../subworkflows/local/input_check'
include { INCLUDE_PROTEINS } from '../subworkflows/local/include_proteins'
include { REFINE_FDR } from '../subworkflows/local/refine_fdr'
include { QUANT } from '../subworkflows/local/quant'
@@ -105,7 +43,10 @@ include { OPENMS_IDMERGER } from '../modules/nf-core/
include { OPENMS_IDSCORESWITCHER } from '../modules/nf-core/openms/idscoreswitcher/main.nf'
include { OPENMS_IDFILTER as OPENMS_IDFILTER_Q_VALUE } from '../modules/nf-core/openms/idfilter/main'
include { MULTIQC } from '../modules/nf-core/multiqc/main'
-include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/custom/dumpsoftwareversions/main'
+include { paramsSummaryMap } from 'plugin/nf-validation'
+include { paramsSummaryMultiqc } from '../subworkflows/nf-core/utils_nfcore_pipeline'
+include { softwareVersionsToYAML } from '../subworkflows/nf-core/utils_nfcore_pipeline'
+include { methodsDescriptionText } from '../subworkflows/local/utils_nfcore_mhcquant_pipeline'
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -113,29 +54,17 @@ include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-// Info required for completion email and summary
-def multiqc_report = []
-
-
-////////////////////////////////////////////////////
-/* -- RUN MAIN WORKFLOW -- */
-////////////////////////////////////////////////////
workflow MHCQUANT {
- ch_versions = Channel.empty()
+ take:
+ ch_samplesheet // channel: samplesheet read in from --input
+ ch_fasta // channel: reference database read in from --fasta
- //
- // SUBWORKFLOW: Check the input file
- //
- INPUT_CHECK (
- file(params.input)
- )
- ch_versions = ch_versions.mix(INPUT_CHECK.out.versions)
- // TODO: OPTIONAL, you can use nf-validation plugin to create an input channel from the samplesheet with Channel.fromSamplesheet("input")
- // See the documentation https://nextflow-io.github.io/nf-validation/samplesheets/fromSamplesheet/
- // ! There is currently no tooling to help you write a sample sheet schema
+ main:
+ ch_versions = Channel.empty()
+ ch_multiqc_files = Channel.empty()
- INPUT_CHECK.out.ms_runs
+ ch_samplesheet
.branch {
meta, filename ->
raw : meta.ext == 'raw'
@@ -147,38 +76,33 @@ workflow MHCQUANT {
other : true }
.set { branched_ms_files }
- // Input fasta file
- Channel.fromPath(params.fasta)
- .map{ fasta -> [[id:fasta.getBaseName()], fasta] }
- .ifEmpty { error ("params.fasta was empty - no input file supplied") }
- .set { fasta_file }
-
//
// SUBWORKFLOW: Include protein information
//
// TODO: Temporary disabled because of outdated vcf parsing
//if (params.include_proteins_from_vcf) {
// // Include the proteins from the vcf file to the fasta file
- // INCLUDE_PROTEINS(fasta_file)
+ // INCLUDE_PROTEINS(ch_fasta)
// ch_versions = ch_versions.mix(INCLUDE_PROTEINS.out.versions)
// ch_fasta_file = INCLUDE_PROTEINS.out.ch_fasta_file
// ch_vcf_from_sheet = INCLUDE_PROTEINS.out.ch_vcf_from_sheet
//} else {
- // ch_fasta_file = fasta_file
+ // ch_fasta_file = ch_fasta
// ch_vcf_from_sheet = Channel.empty()
//}
if (!params.skip_decoy_generation) {
// Generate reversed decoy database
- OPENMS_DECOYDATABASE(fasta_file)
+ OPENMS_DECOYDATABASE(ch_fasta)
ch_versions = ch_versions.mix(OPENMS_DECOYDATABASE.out.versions)
ch_decoy_db = OPENMS_DECOYDATABASE.out.decoy_fasta
.map{ meta, fasta -> [fasta] }
} else {
- ch_decoy_db = fasta_file.map{ meta, fasta -> [fasta] }
+ ch_decoy_db = ch_fasta.map{ meta, fasta -> [fasta] }
}
- // If mzml files are specified, they are encapsulated in a list [meta, [mzml]]. We need to extract the path for grouping later
- ch_ms_files = branched_ms_files.mzml.map{ meta, mzml -> [meta, mzml[0]]}
+ // Initialize channel for ms files that do not need to be converted
+ ch_ms_files = branched_ms_files.mzml
+
// Raw file conversion
THERMORAWFILEPARSER(branched_ms_files.raw)
ch_versions = ch_versions.mix(THERMORAWFILEPARSER.out.versions)
@@ -212,7 +136,7 @@ workflow MHCQUANT {
//if (params.include_proteins_from_vcf) {
// OPENMS_COMETADAPTER(ch_clean_mzml_file.join(ch_decoy_db, remainder:true))
//} else {
- // OPENMS_COMETADAPTER(ch_clean_mzml_file.combine(ch_fasta_file.map{ meta, fasta -> [fasta] }))
+ // OPENMS_COMETADAPTER(ch_clean_mzml_file.combine(ch_fasta.map{ meta, fasta -> [fasta] }))
//}
OPENMS_COMETADAPTER(ch_clean_mzml_file.combine(ch_decoy_db))
ch_versions = ch_versions.mix(OPENMS_COMETADAPTER.out.versions)
@@ -223,12 +147,12 @@ workflow MHCQUANT {
// Save indexed runs for later use to keep meta-run information. Sort based on file id
OPENMS_PEPTIDEINDEXER.out.idxml
- .map { meta, idxml -> [ groupKey([id: meta.sample + '_' + meta.condition], meta.group_count), meta] }
+ .map { meta, idxml -> [ groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count), meta] }
.groupTuple()
.set { merge_meta_map }
OPENMS_PEPTIDEINDEXER.out.idxml
- .map { meta, idxml -> [ groupKey([id: meta.sample + '_' + meta.condition], meta.group_count), idxml] }
+ .map { meta, idxml -> [ groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count), idxml] }
.groupTuple()
.set { ch_runs_to_merge }
@@ -238,7 +162,7 @@ workflow MHCQUANT {
// Run MS2Rescore
ch_clean_mzml_file
- .map { meta, mzml -> [ groupKey([id: meta.sample + '_' + meta.condition], meta.group_count), mzml] }
+ .map { meta, mzml -> [ groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count), mzml] }
.groupTuple()
.join(OPENMS_IDMERGER.out.idxml)
.map { meta, mzml, idxml -> [meta, idxml, mzml, []] }
@@ -299,7 +223,7 @@ workflow MHCQUANT {
if (params.annotate_ions) {
// Join the ch_filtered_idxml and the ch_mzml_file
- ch_clean_mzml_file.map { meta, mzml -> [ groupKey([id: meta.sample + '_' + meta.condition], meta.group_count), mzml] }
+ ch_clean_mzml_file.map { meta, mzml -> [ groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count), mzml] }
.groupTuple()
.join(ch_filter_q_value)
.set{ ch_ion_annotator_input }
@@ -354,61 +278,36 @@ workflow MHCQUANT {
//}
//
- // MODULE: Pipeline reporting
+ // Collate and save software versions
//
- CUSTOM_DUMPSOFTWAREVERSIONS (
- ch_versions.unique().collectFile(name: 'collated_versions.yml')
- )
+ softwareVersionsToYAML(ch_versions)
+ .collectFile(storeDir: "${params.outdir}/pipeline_info", name: 'nf_core_pipeline_software_mqc_versions.yml', sort: true, newLine: true)
+ .set { ch_collated_versions }
//
// MODULE: MultiQC
//
- if (!params.skip_multiqc) {
- workflow_summary = WorkflowMhcquant.paramsSummaryMultiqc(workflow, summary_params)
- ch_workflow_summary = Channel.value(workflow_summary)
-
- methods_description = WorkflowMhcquant.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description, params)
- ch_methods_description = Channel.value(methods_description)
-
- ch_multiqc_files = Channel.empty()
- ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml'))
- ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml'))
- ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect())
-
- MULTIQC (
- ch_multiqc_files.collect(),
- ch_multiqc_config.collect().ifEmpty([]),
- ch_multiqc_custom_config.collect().ifEmpty([]),
- ch_multiqc_logo.collect().ifEmpty([])
- )
- multiqc_report = MULTIQC.out.report.toList()
- ch_versions = ch_versions.mix(MULTIQC.out.versions)
- }
-
-}
-
-/*
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- COMPLETION EMAIL AND SUMMARY
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-*/
-
-workflow.onComplete {
- if (params.email || params.email_on_fail) {
- NfcoreTemplate.email(workflow, params, summary_params, projectDir, log, multiqc_report)
- }
- NfcoreTemplate.dump_parameters(workflow, params)
- NfcoreTemplate.summary(workflow, params, log)
- if (params.hook_url) {
- NfcoreTemplate.IM_notification(workflow, params, summary_params, projectDir, log)
- }
-}
+ ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true)
+ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config, checkIfExists: true) : Channel.empty()
+ ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath(params.multiqc_logo, checkIfExists: true) : Channel.empty()
+ summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json")
+ ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params))
+ ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true)
+ ch_methods_description = Channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description))
+ ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml'))
+ ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions)
+ ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml', sort: false))
+
+ MULTIQC (
+ ch_multiqc_files.collect(),
+ ch_multiqc_config.toList(),
+ ch_multiqc_custom_config.toList(),
+ ch_multiqc_logo.toList()
+ )
-workflow.onError {
- if (workflow.errorReport.contains("Process requirement exceeds available memory")) {
- println("🛑 Default resources exceed availability 🛑 ")
- println("💡 See here on how to configure pipeline: https://nf-co.re/docs/usage/configuration#tuning-workflow-resources 💡")
- }
+ emit:
+ multiqc_report = MULTIQC.out.report.toList() // channel: /path/to/multiqc_report.html
+ versions = ch_versions // channel: [ path(versions.yml) ]
}
/*