Skip to content

feat(#208): add multisample pipeline QC report - #216

Open
Jonahnki wants to merge 9 commits into
devfrom
feat/208-pipeline-qc
Open

Jonahnki wants to merge 9 commits into
devfrom
feat/208-pipeline-qc

Conversation

@Jonahnki

Copy link
Copy Markdown

Summary
Adds per-stage QC collection modules that emit structured JSON at each pipeline stage, with a collector process that renders MultiQC custom content YAML files for the final report. Gives users cross-sample visibility into bundle metrics, segmentation quality and stage-level statistics without manual inspection.

What changed:

  • New bin/emit_bundle_qc.py extracts read-level and experiment-level metrics from Xenium bundles
  • New bin/emit_segmentation_qc.py computes cell count, transcript assignment rate and per-cell statistics from Baysor output
  • New bin/emit_pipeline_qc.py collects all per-stage QC JSONs and renders MultiQC custom content (general stats table + bargraph + per-stage detail tables)
  • New Nextflow modules: EMIT_BUNDLE_QC, EMIT_SEGMENTATION_QC, COLLECT_PIPELINE_QC
  • Wire QC collection into spatialaxe.nf for all mode/method combinations
  • Add segmentation_csv emit to cellpose_baysor, baysor_transcripts and baysor_prior_mask subworkflows
  • Add section ordering to multiqc_config.yml for pipeline QC panels
  • Add publishDir config for all QC modules in modules.config

Architecture:
Each pipeline stage emits a self-contained QC JSON ({sample_id, stage, metrics}). The COLLECT_PIPELINE_QC process gathers all JSONs and produces MultiQC custom content YAML files that slot into the existing report. QC channel (ch_pipeline_qc_reports) is kept separate from upstream ch_qc_reports to avoid breaking existing OPT QC functionality.

MultiQC sections added:

  • Pipeline Summary -> Cross-sample general stats table
  • Stage Metrics -> Bargraph of key metrics per stage
  • Segmentation -> Per-sample segmentation detail table
  • Bundle QC -> Raw bundle metrics
  • Bundle QC Post -> Post-segmentation bundle metrics

Many thanks for the chance of contributing to nf-core/spatialaxe! Pls, let me know should there be any necessary PR changes on my path!

heylf and others added 4 commits August 6, 2026 15:53
Switch fix-linting to the hardened nf-core/actions reusable workflow
Add per-stage QC collection modules that emit structured JSON, with a
collector that produces MultiQC custom content YAML files for the
final report.

- New bin/emit_bundle_qc.py, emit_segmentation_qc.py, emit_pipeline_qc.py
- New Nextflow modules: EMIT_BUNDLE_QC, EMIT_SEGMENTATION_QC,
  COLLECT_PIPELINE_QC
- Wire QC collection into spatialaxe.nf for all mode/method combinations
- Add segmentation_csv emit to cellpose_baysor, baysor_transcripts, and
  baysor_prior_mask subworkflows
- Add section ordering to multiqc_config.yml
- Add publishDir config for all QC modules in modules.config
@github-actions

Copy link
Copy Markdown

This PR is against the master branch ❌

  • Do not close this PR
  • Click Edit and change the base to dev
  • This CI test will remain failed until you push a new commit

Hi @Jonahnki,

It looks like this pull-request is has been made against the nf-core/spatialaxe master branch.
The master branch on nf-core repositories should always contain code from the latest release.
Because of this, PRs to master are only allowed if they come from the nf-core/spatialaxe dev branch.

You do not need to close this PR, you can change the target branch to dev by clicking the "Edit" button at the top of this page.
Note that even after this, the test will continue to show as failing until you push a new commit.

Thanks again for your contribution!

@github-actions

Copy link
Copy Markdown

Warning

Newer version of the nf-core template is available.

Your pipeline is using an old version of the nf-core template: 4.0.3.
Please update your pipeline to the latest version.

For more documentation on how to update your pipeline, please see the Synchronisation documentation.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

nf-core pipelines lint overall result: Passed ✅ ⚠️

Posted for pipeline commit 67363a2

+| ✅ 292 tests passed       |+
#| ❔   8 tests were ignored |#
!| ❗   7 tests had warnings |!
Details

❗ Test warnings:

  • files_exist - File not found: conf/igenomes.config
  • files_exist - File not found: conf/igenomes_ignored.config
  • pipeline_todos - TODO string in CHANGELOG.md: ## 1.0.1dev - [unreleased replace with date on release ]
  • pipeline_todos - TODO string in main.nf: Optionally add in-text citation tools to this list.
  • pipeline_todos - TODO string in main.nf: Optionally add bibliographic entries to this list.
  • schema_lint - Schema 'description' should be 'A pipeline for spatialomics 10x Xenium In Situ data.'
    Found: 'A pipeline to process spatialomics data from 10x Xenium In Situ or 10x Atera.'
  • schema_description - No description provided in schema for parameter: spoqc

❔ Tests ignored:

  • files_exist - File is ignored: .github/workflows/awsfulltest.yml
  • files_exist - File is ignored: .github/workflows/awstest.yml
  • files_exist - File is ignored: .github/workflows/linting_comment.yml
  • files_unchanged - File ignored due to lint config: .github/PULL_REQUEST_TEMPLATE.md
  • files_unchanged - File ignored due to lint config: assets/nf-core-spatialaxe_logo_light.png
  • files_unchanged - File ignored due to lint config: docs/images/nf-core-spatialaxe_logo_light.png
  • files_unchanged - File ignored due to lint config: docs/images/nf-core-spatialaxe_logo_dark.png
  • files_unchanged - File ignored due to lint config: .gitignore or .prettierignore

✅ Tests passed:

Run details

  • nf-core/tools version 4.1.0
  • Run at 2026-10-17 06:18:46

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

❌ nf-test failed with latest Nextflow version

Note

Tests with Nextflow's latest version failed but it will not cause a CI workflow failure.
Please check if the failure is expected with newer (edge-)releases of Nextflow or if it needs fixing.

  • ❌ docker | latest-everything | Shard 6/9

See the full run for details.

@heylf
heylf changed the base branch from master to dev October 15, 2026 13:58
@heylf

heylf commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

@Jonahnki thank you for your contribution. Just make always sure to point towards the dev branch. I have already changed it. Just for the future.

Comment thread bin/emit_segmentation_qc.py Outdated

def compute_segmentation_metrics(
segmentation_csv: Path,
transcripts_csv: Path | None = None,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not used. Remove or check again.

Comment thread conf/modules.config Outdated
]
}

// ---------------------------- pipeline QC (Issue #208) ----------------------------------

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ---------------------------- pipeline QC (Issue #208) ----------------------------------
// ---------------------------- pipeline QC ----------------------------------

Comment thread bin/emit_segmentation_qc.py Outdated

import argparse
import json
import sys

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import sys

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sys is not used


script:
"""
pip install --quiet pyyaml 2>/dev/null || true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please dont do things like this. You have pyyaml anyway already covered in your container. If you need to install more packages then always put it into the docker. So please remove the line.

Comment on lines +23 to +44
mkdir -p qc_input mqc_output

# Stage all QC JSONs into a single directory
for f in ${qc_jsons}; do
cp "\$f" qc_input/
done

# Also collect the merged records for downstream use
python3 -c "
import json, pathlib
records = []
for p in sorted(pathlib.Path('qc_input').glob('*_qc.json')):
try:
data = json.loads(p.read_text())
if isinstance(data, list):
records.extend(data)
else:
records.append(data)
except Exception:
pass
pathlib.Path('all_qc_records.json').write_text(json.dumps(records, indent=2))
"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can all be removed if you take the direct file paths which you get from path(qc_jsons). Instead of a wildcard you then go over all files of path(qc_jsons) in emit_pipeline_qc.py. What I mean is that path(qc_jsons) is a list of paths which you can provide as an arguemnt for emit_pipeline_qc.py directly.

So basically it would be better if emit_pipeline_qc.py handles the json agglomeration directly and that this step is not handled by some python code in the main.nf.

@heylf heylf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some requests to change a bit of code.

- Drop unused sys import and dead transcripts_csv param in emit_segmentation_qc.py (no caller ever passed it)
- Simplify modules.config comment per review suggestion
- Replace runtime pip install + inline python3 -c merge logic in COLLECT_PIPELINE_QC with a direct call to emit_pipeline_qc.py
- emit_pipeline_qc.py: --qc-dir (glob) replaced with --qc-files (explicit file list); script now writes the merged output itself via --merged-output
@Jonahnki

Copy link
Copy Markdown
Author

Thanks for the review — addressed all four points:

  1. Removed the unused sys import and the dead transcripts_csv parameter from compute_segmentation_metrics() in emit_segmentation_qc.py (confirmed via a repo-wide grep that no caller ever passed this argument).

  2. Simplified the modules.config comment as suggested.

  3. Replaced the runtime pip install --quiet pyyaml + inline python3 -c merge logic in COLLECT_PIPELINE_QC with a direct call to emit_pipeline_qc.py.

  4. emit_pipeline_qc.py: replaced --qc-dir (directory + glob) with an explicit --qc-files file list, and the script now writes the merged all_qc_records.json output itself via --merged-output.

Verified with synthetic multi-sample/multi-stage QC JSON producing the correct merged output and all 4 MultiQC YAML sections, including a malformed-JSON edge case that skips gracefully. --qc-dir had exactly one caller in the whole repo (this module), so the rename was safe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants