Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions modules/nf-core/qcatch/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json
channels:
- conda-forge
- bioconda

dependencies:
- bioconda::qcatch=0.2.8
58 changes: 58 additions & 0 deletions modules/nf-core/qcatch/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
process QCATCH {
tag "$meta.id"
label 'process_low'

conda "${moduleDir}/environment.yml"
container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
'oras://community.wave.seqera.io/library/qcatch:0.2.8--3089b62e628f96d7':
'community.wave.seqera.io/library/qcatch:0.2.8--454a9b478b62c36f' }"
input:
tuple val(meta), val(chemistry), path(quant_dir)

output:
tuple val(meta), path("*.html") , emit: report
tuple val(meta), path("*_filtered_quants.h5ad") , emit: filtered_h5ad
tuple val(meta), path("*_metrics_summary.csv") , emit: metrics_summary
path "versions.yml" , emit: versions

when:
task.ext.when == null || task.ext.when

script:
def args = task.ext.args ?: ''
def prefix = task.ext.prefix ?: "${meta.id}"

"""
qcatch \\
--input ${quant_dir} \\
--output ${prefix} \\
--chemistry ${chemistry} \\
--save_filtered_h5ad \\
--export_summary_table \\
${args}

mv ${prefix}/QCatch_report.html ${prefix}_qcatch_report.html
mv ${prefix}/filtered_quants.h5ad ${prefix}_filtered_quants.h5ad
mv ${prefix}/summary_table.csv ${prefix}_metrics_summary.csv

cat <<-END_VERSIONS > versions.yml
"${task.process}":
qcatch: \$(qcatch --version | sed -e "s/qcatch //g")
END_VERSIONS
"""

stub:
def args = task.ext.args ?: ''
def prefix = task.ext.prefix ?: "${meta.id}"

"""
touch ${prefix}_qcatch_report.html
touch ${prefix}_filtered_quants.h5ad
touch ${prefix}_metrics_summary.csv

cat <<-END_VERSIONS > versions.yml
"${task.process}":
qcatch: \$(qcatch --version | sed -e "s/qcatch //g")
END_VERSIONS
"""
}
86 changes: 86 additions & 0 deletions modules/nf-core/qcatch/meta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: qcatch
description: Cell-filtering and QC reporting tool for alevin-fry quantification results
keywords:
- single-cell
- quality control
- alevin-fry
- cell filtering
- QC report
tools:
- qcatch:
description: |
QCatch is a quality control and cell filtering tool designed for single-cell RNA-seq data processed by alevin-fry.
It generates comprehensive QC reports and filtered count matrices.
homepage: https://github.com/COMBINE-lab/QCatch
licence: ["BSD-3-Clause"]
identifier: ""
input:
- - meta:
type: map
description: |
Groovy Map containing sample information
e.g. [ id:'test', single_end:false ]
- chemistry:
type: string
description: |
Chemistry type for the single-cell experiment, which determines the partition range for the empty_drops step.
Supported values: '10X_3p_v2', '10X_3p_v3', '10X_3p_v4', '10X_5p_v3', '10X_3p_LT', '10X_HT'.
If using a standard 10X chemistry and quantification was performed with simpleaf (v0.19.5 or later),
QCatch will try to infer the correct chemistry from the metadata.
- quant_dir:
type: directory
description: |
Directory containing alevin-fry quantification results (af_quant output from simpleaf quant).
Must contain the quantification matrix and associated metadata files.
ontologies: []
output:
report:
- - meta:
type: map
description: |
Groovy Map containing sample information
e.g. [ id:'test', single_end:false ]
- "*.html":
type: file
description: |
HTML QC report generated by QCatch containing visualizations and metrics
pattern: "*.html"
ontologies: []
filtered_h5ad:
- - meta:
type: map
description: |
Groovy Map containing sample information
e.g. [ id:'test', single_end:false ]
- "*_filtered_quants.h5ad":
type: file
description: |
Filtered quantification matrix in h5ad format (AnnData)
pattern: "*_filtered_quants.h5ad"
ontologies: []
metrics_summary:
- - meta:
type: map
description: |
Groovy Map containing sample information
e.g. [ id:'test', single_end:false ]
- "*_metrics_summary.csv":
type: file
description: |
CSV file containing summary metrics from QC analysis
pattern: "*_metrics_summary.csv"
ontologies: []
versions:
- versions.yml:
type: file
description: |
File containing software versions
pattern: "versions.yml"
ontologies:
- edam: http://edamontology.org/format_3750 # YAML
authors:
- "@wzheng0520"
- "@dongzehe"
maintainers:
- "@wzheng0520"
- "@dongzehe"
70 changes: 70 additions & 0 deletions modules/nf-core/qcatch/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
nextflow_process {

name "Test Process QCATCH"
script "../main.nf"
process "QCATCH"
config "./nextflow.config"

tag "modules"
tag "modules_nfcore"
tag "qcatch"
tag "unzip"

test("test_qcatch - 1k_pbmc_v3") {

setup {
// Download and extract QCatch test data from COMBINE-lab using UNZIP module
run("UNZIP") {
script "modules/nf-core/unzip/main.nf"
process {
"""
meta = [id:'qcatch_test_data']
input[0] = Channel.of([meta, file('https://umd.box.com/shared/static/zd4sai70uw9fs24e1qx6r41ec50pf45g.zip')])
"""
}
}
}

when {
process {
"""
// Get the 1k_pbmc_v3 subdirectory from the extracted archive
input[0] = UNZIP.out.unzipped_archive.map { meta, dir ->
def quant_dir = file("\${dir}/test_data/1k_pbmc_v3")
[[id:'1k_pbmc_v3'], '10X_3p_v3', quant_dir]
}
"""
}
}

then {
assertAll(
{ assert process.success },
{ assert file(process.out.report.get(0).get(1)).exists() },
{ assert file(process.out.filtered_h5ad.get(0).get(1)).exists() },
{ assert file(process.out.metrics_summary.get(0).get(1)).exists() },
{ assert snapshot(process.out.versions).match() }
)
}
}

test("test_qcatch - stub") {
options "-stub-run"

when {
process {
"""
meta = [id:'test_stub']
input[0] = Channel.of([meta, '10X_3p_v3', file('stub_quant_dir')])
"""
}
}

then {
assertAll(
{ assert process.success },
{ assert snapshot(process.out).match() }
)
}
}
}
79 changes: 79 additions & 0 deletions modules/nf-core/qcatch/tests/main.nf.test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"test_qcatch - 1k_pbmc_v3": {
"content": [
[
"versions.yml:md5,46f73aa6be1b23a13365040ab92e22af"
]
],
"meta": {
"nf-test": "0.9.3",
"nextflow": "25.10.3"
},
"timestamp": "2026-02-13T21:36:15.677759721"
},
"test_qcatch - stub": {
"content": [
{
"0": [
[
{
"id": "test_stub"
},
"test_stub_qcatch_report.html:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"1": [
[
{
"id": "test_stub"
},
"test_stub_filtered_quants.h5ad:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"2": [
[
{
"id": "test_stub"
},
"test_stub_metrics_summary.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"3": [
"versions.yml:md5,46f73aa6be1b23a13365040ab92e22af"
],
"filtered_h5ad": [
[
{
"id": "test_stub"
},
"test_stub_filtered_quants.h5ad:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"metrics_summary": [
[
{
"id": "test_stub"
},
"test_stub_metrics_summary.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"report": [
[
{
"id": "test_stub"
},
"test_stub_qcatch_report.html:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"versions": [
"versions.yml:md5,46f73aa6be1b23a13365040ab92e22af"
]
}
],
"meta": {
"nf-test": "0.9.3",
"nextflow": "25.10.2"
},
"timestamp": "2026-02-13T21:56:57.673052701"
}
}
5 changes: 5 additions & 0 deletions modules/nf-core/qcatch/tests/nextflow.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
env {
MPLCONFIGDIR = "./tmp"
NUMBA_CACHE_DIR = "./tmp"
NUMBA_DISABLE_CACHE = 1
}