Contributing a Tool
This guide explains how to add a bioinformatics tool to the Tools Catalog. A tool entry tells Depictio how to recognise a pipeline's output files and turn them into ready-made dashboard renders — so users get visualizations automatically, with no manual wiring.
Adding a tool is a single-folder pull request under
depictio/catalog/<tool>/ — no Depictio internals to learn, and no Python
unless an output needs reshaping. Everything is validated in CI by
depictio dev catalog validate.
Two ways in (v1.9.0+)¶
-
In the browser
Tool Studio does all of the below without YAML: drop one output file, bind its columns in depictio's own component builder, and the app opens the pull request for you.
Use it when your output file is already a table with a header row.
-
By hand
Write the three files yourself, following this page. You need this when the output has to be reshaped first, so the entry carries a recipe.
About half the committed catalog outputs are in this case.
Both routes produce the same folder and pass the same CI check, so switching between them costs nothing.
The files¶
A tool is one folder: module.yaml identifies it, and the rest repeat per
output file.
| File | One per | Purpose |
|---|---|---|
module.yaml |
tool | Tool identity: id, name, description, homepage, biotools_url, nf_core_url. |
<output>.yaml |
output | find the file, optional recipe, and the renders_as it offers. |
<output>.tsv |
output | Fixture — a small sample, so CI previews & checks every render. |
<output>.py |
output · optional | Recipe — only when the raw file needs reshaping. |
The .tsv fixture and .py recipe are not alternatives — they do different
jobs and a tool often ships both (the ivar example below has both). The recipe
is the only optional piece: skip it when the raw file is already tidy.
A real single-output tool (depictio/catalog/ivar/):
ivar/
├── module.yaml # id, name, description, homepage, biotools_url, nf_core_url
├── variants_long.yaml # find + recipe + renders_as
├── variants_long.py # recipe: reshape variants_long_table.csv
└── variants_long.tsv # fixture
Step 1 — module.yaml (identity)¶
module.yaml declares the identity the tool's catalog card shows. Nothing reads
the nf-core meta.yml at runtime: the catalog takes these fields from
module.yaml alone, so copy them from meta.yml when you write the file.
id: ivar
name: iVar
nf_core_url: https://github.com/nf-core/modules/tree/master/modules/nf-core/ivar/variants
biotools_url: https://bio.tools/andersen-lab_ivar # from nf-core meta.yml identifier
description: >-
Functions for viral amplicon sequencing: primer trimming, variant calling
and consensus building.
homepage: https://github.com/andersen-lab/ivar
stage: variants # where its outputs sit in a pipeline (v1.16.0+)
| Field | Required | Where to take it from |
|---|---|---|
id, name |
yes | The folder name and the display label. |
description |
yes | One or two sentences, usually the tool's description in meta.yml. |
homepage |
yes | homepage in meta.yml; when it is empty, tool_dev_url, then documentation. |
biotools_url |
yes, may be null |
https://bio.tools/<id> from the biotools: identifier. Write biotools_url: null when bio.tools has no entry, so the gap is explicit rather than forgotten. |
nf_core_url |
when an nf-core module exists | The module the outputs come from. CI checks it against the vendored nf-core index. |
edam_topics |
no | Full EDAM URLs. |
stage |
no | Where the tool's outputs sit in a pipeline: qc, alignment, quantification, peaks, variants, fusions, taxonomy, annotation, immune, differential, benchmarking or other (the default). A composed dashboard opens one tab per stage present, in this order. Every bundled tool declares one (v1.16.0+). |
depictio dev catalog validate rejects a module with no description or
homepage, and the catalog tests reject one that leaves biotools_url out.
For a tool with no single nf-core module (e.g. QIIME 2), declare the same
identity here and set nf_core_url per output instead.
Step 2 — <output>.yaml (one per output file)¶
id: ivar_variants_long # globally-unique; used as `use: ivar/variants_long`
mode: variants
description: Per-sample annotated variant calls (long table).
find: { filename: "variants_long_table.csv" } # recognise the raw file
recipe: ivar/variants_long.py # optional — owns the output columns
fixture: variants_long.tsv # co-located bindable sample
renders_as:
- { id: manhattan, component: advanced_viz, kind: manhattan, roles: {chr: CHROM, pos: POS, score: AF} }
- { component: figure, visu_type: histogram, dict_kwargs: {x: AF, color: EFFECT} }
- { component: card, column: AF, aggregation: average }
- { component: table }
find — recognise the file by name or path:
find: { filename: "variants_long_table.csv" } # glob on the basename
find: { path_glob: "**/mosdepth/genome/*.coverage.tsv" } # glob on the path (supports **)
renders_as — each entry binds the output's columns to a dashboard
component (see the YAML above for full examples). Besides component, each takes:
| Component | Keys |
|---|---|
advanced_viz |
kind + roles |
figure (UI) |
visu_type + dict_kwargs |
figure (code) |
code (Python that sets fig from df + px) |
card |
column + aggregation |
table |
— |
multiqc |
section |
Give an advanced_viz render an optional id: to address it from a dashboard as
use: <tool>/<id> — e.g. the id: manhattan in the YAML above is referenced as
use: ivar/manhattan.
Schema-ownership rule: if you set
recipe, the recipe owns the columns — do not also declarecolumns. If the raw file is already tidy and bindable, skip the recipe and declarecolumns: {NAME: Dtype, …}(polars dtypes) instead. Validation rejects setting both.
Composition hints (v1.16.0+)¶
When depictio ingest composes a dashboard
from the catalog, three optional fields decide where an output's renders go:
| Field | On | Effect |
|---|---|---|
stage |
the output | Overrides the tool's stage for this output, e.g. a MultiQC section carrying STAR's numbers is alignment, not MultiQC's qc |
headline: true |
a card render |
One of the run's key metrics: the card also goes to the composed Overview and, when its table has a sample column, its value per sample to the general statistics table, the way MultiQC's General Statistics picks a few columns per tool. Mark one or two per tool |
priority |
any render | An integer ≥ 0: lower comes first within its output. Renders without one keep their declared order, after those with one |
renders_as:
- { component: card, column: frip_score, aggregation: average, secondary_layout: gauge, coverage_max: 1.0, headline: true }
There is no separate threshold for the Overview: a headline card keeps its own
threshold_value, threshold_direction and threshold_warn there.
Step 3 — Recipe (only if the file needs reshaping)¶
A recipe is a small polars module co-located as <output>.py. It declares its
inputs, its output schema, and a transform:
"""Reshape variants_long_table.csv into tidy, bindable columns."""
import polars as pl
from depictio.models.models.transforms import RecipeSource
# INPUT SCHEMA: the columns each source must contain, checked before transform().
SOURCES: list[RecipeSource] = [
RecipeSource(
ref="variants_raw",
glob_pattern="variants/*/variants_long_table.csv",
format="CSV",
input_schema={
"sample": pl.Utf8, "CHROM": pl.Utf8, "POS": pl.Int64,
"AF": pl.Float64, "GENE": pl.Utf8, "EFFECT": pl.Utf8,
},
),
]
# OUTPUT SCHEMA: the columns transform() returns, checked after it.
OUTPUT_SCHEMA: dict[str, type[pl.DataType]] = {
"sample": pl.Utf8, "CHROM": pl.Utf8, "POS": pl.Int64,
"AF": pl.Float64, "GENE": pl.Utf8, "EFFECT": pl.Utf8,
}
def transform(sources: dict[str, pl.DataFrame]) -> pl.DataFrame:
df = sources["variants_raw"]
return df.select(OUTPUT_SCHEMA.keys()) # exactly the OUTPUT_SCHEMA columns
OUTPUT_SCHEMA is what the catalog grounds every render binding against. This
is the same recipe contract used by templates. To
see a recipe's output columns while writing roles:
depictio dev catalog columns ivar/variants_long.py.
Step 4 — Fixture¶
Save a small, real sample of the output as <output>.tsv (.csv / .parquet
also work), co-located in the folder. It must contain every column your
renders_as binds to — CI reads its real columns to validate (and preview)
each render.
Step 5 — Validate, preview, and open a PR¶
# Validate just your tool (load → find → recipe → render bindings)
depictio dev catalog validate --path depictio/catalog/<tool>
# Live-preview an output rendered on its fixture
depictio catalog preview <tool>_<output>
# Helpful while authoring
depictio catalog list # every tool + output + render targets
depictio catalog info <tool> # one tool in detail
depictio dev catalog match /path/run # which outputs are recognised in a run
Tip — add this header to each catalog YAML for live validation and autocomplete in your editor:
# yaml-language-server: $schema=https://raw.githubusercontent.com/depictio/depictio/main/depictio/catalog/catalog.schema.json
Before submitting, check:
-
module.yamlhasid,name,description,homepage,biotools_url(or an explicitnull), and (where applicable)nf_core_url. - Each
<output>.yamlhas a uniqueid, afind, and at least onerenders_as. - On outputs that bind columns, set
recipeorcolumns(declare one) — never both. - A fixture is committed for every output that binds columns, covering all bound columns.
-
depictio dev catalog validate --path depictio/catalog/<tool>passes green.
In the PR, link the tool's nf-core module / homepage and note the pipeline whose output you tested against.
Reference¶
- In-repo:
depictio/catalog/README.md(overview) anddepictio/catalog/SCHEMA.md(full field reference). - The component config reference: Dashboard Components.
- Questions: GitHub Discussions — tag your PR
catalog.