Skip to content
Merged
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
83 changes: 83 additions & 0 deletions .github/CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement: the maintainer, [@JoshuaJewell](https://github.com/JoshuaJewell), using the contact details on their GitHub profile. All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series of actions.

**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].

Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].

For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].

[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
59 changes: 59 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Bug report
description: Something in MetaManifold does not work as documented.
labels: [bug]
body:
- type: markdown
attributes:
value: |
Search the existing issues first. Raise issues about a fork's own code on that fork.
- type: textarea
id: steps
attributes:
label: Steps or command
description: The command you ran, or the steps in the web interface, so that someone else can repeat them.
validations:
required: true
- type: textarea
id: expected
attributes:
label: What you expected
validations:
required: true
- type: textarea
id: actual
attributes:
label: What happened
description: Include the error message, the server log lines, or a screenshot.
validations:
required: true
- type: input
id: julia
attributes:
label: Julia version
placeholder: output of `julia --version`
validations:
required: true
- type: input
id: r
attributes:
label: R version
placeholder: output of `R --version | head -1`
validations:
required: true
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- type: input
id: browser
attributes:
label: Browser and version
validations:
required: true
- type: input
id: commit
attributes:
label: MetaManifold commit
placeholder: output of `git rev-parse --short HEAD`
- type: textarea
id: config
attributes:
label: Relevant configuration
description: The keys from `pipeline.yml` or the run's `run_config.yml` that bear on the problem.
render: yaml
41 changes: 41 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Feature request
description: Suggest a new feature or a change to an existing one.
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Search the existing issues first. To change an architectural decision, describe the change
here before writing any code. The settled decisions in CONTRIBUTING.md can be discussed in an
issue, but a pull request does not reopen them.
- type: textarea
id: problem
attributes:
label: Problem
description: What you are trying to do, and what stops you now.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed change
description: What MetaManifold should do instead. For a statistical method, name the method and a reference for it.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
- type: dropdown
id: area
attributes:
label: Area
options:
- Pipeline stages
- Analysis and statistics
- Web interface
- Configuration
- Installation and toolchain
- Other
validations:
required: true
43 changes: 43 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
## Summary

<!-- What does this PR do, and why? One logical change per PR. -->

Closes #

## Type of change

- [ ] 🐛 Bug fix (non-breaking change that fixes an issue)
- [ ] ✨ New feature (non-breaking change that adds functionality)
- [ ] 💥 Breaking change (would change existing behaviour, a config key or an API response)
- [ ] 📊 Statistics fix (a method returned a wrong number, or a result it could not support)
- [ ] 📖 Documentation
- [ ] 🧹 Refactor / tech debt (behaviour-preserving)
- [ ] ⚡ Performance
- [ ] 🔧 Build / CI / tooling

## How has this been verified?

<!-- Name the commands you ran and what they reported. For example:
julia --project=. test/runtests.jl
julia --project=. -t 2 test/runtests.jl --integration --server
cd frontend && bun run test -->

Tested with Julia `…`, R `…` and bun `…`.

## What this leaves out

<!-- Anything related that this PR does not do, and why. -->

## Checklist

- [ ] My branch starts at the current tip of `main` (`behind_by` is `0`; see CONTRIBUTING.md) and has no merge commits from `main`.
- [ ] I ran `julia --project=. test/runtests.jl` (and `cd frontend && bun run test` for frontend changes) and they pass.
- [ ] CI ran on this PR and passes.
- [ ] No generated or vendored files, and no lockfile change the work does not need (or the reason is given above).
- [ ] New code is AGPL-3.0, or MPL-2.0 with an SPDX identifier in each file I wrote. I left existing file headers, `LICENSE`, `CITATION.cff` and the licence and acknowledgement sections of `README.md` unchanged.
- [ ] Every number shown to a user is computed from their data. When a method cannot run it raises an explicit error.
- [ ] Docs are updated, and no claim in them overstates what the code does.

## Notes for reviewers

<!-- Anything that needs special attention, follow-up, or context. To be considered for authorship in CITATION.cff, say so here. -->
79 changes: 75 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,16 @@ Once a run completes, analysis is performed on request through the web UI, both
- Pipeline stage read-count summaries
- NMDS ordination (Bray-Curtis, via R/vegan)
- PERMANOVA, with PERMDISP to check for differences in dispersion (via R/vegan)
- Differential abundance between two conditions: a negative-binomial model per taxon (via R/MASS) with Benjamini-Hochberg adjusted p-values (see [Differential abundance](#differential-abundance))

Counts may be normalised before analysis (none, rarefaction to a fixed or auto-resolved depth, or scaling with ranked subsampling (SRS), which reaches the depth by scaling counts and keeps more of the community structure), and contamination-flagged taxa may be included or excluded. Ordinations can also apply a Hellinger transform before the Bray-Curtis dissimilarity. Composition charts can be drawn as a grid faceted on any two of run, group and sub-group. All analysis charts are returned as Plotly JSON and rendered interactively in the browser.

## Documentation

Longer documentation for users, maintainers, developers and theorists is kept as a wiki in
[metadatastician/MetaManifold-Evidence, under `docs/wikis/`](https://github.com/metadatastician/MetaManifold-Evidence/tree/main/docs/wikis).
Start at its [Home page](https://github.com/metadatastician/MetaManifold-Evidence/blob/main/docs/wikis/Home.md).

## Prerequisites

- **Julia** 1.12 (installed by `install.sh` if missing, and pinned to the Manifest version for this directory)
Expand Down Expand Up @@ -240,6 +247,67 @@ The composition view classifies each ASV/OTU into a biological category and rend
<img src=".github/screenshots/composition.png" width="800" alt="Organism-category composition of two runs as stacked bars">
</p>

### Differential abundance

The Differential Abundance tab of a study's analysis workspace tests each taxon
for a difference in abundance between two conditions. Select exactly two runs or
pooled-run sub-groups, a results table and a rank. The first condition is the
reference, so a positive fold change means the taxon is more abundant in the
second. The rank list shows only the ranks the selected runs share. With
aggregation on, a pooled run counts as one condition rather than one per
sub-group.

Counts are summed per taxon at the chosen rank, with rows that have no name at
that rank pooled as `Unclassified`. This happens after the read-count bounds and
any `analysis.exclude_categories` entry that applies to `differential`. Each
taxon is fitted with its own negative-binomial GLM, `MASS::glm.nb(y ~ group +
offset(log(size factor)))`, on the raw integer read counts. There are no
pseudocounts. The group coefficient gets a Wald test, and Benjamini-Hochberg
adjustment runs over the taxa that produced a p-value. Size factors come from
the whole count matrix and are centred to a geometric mean of 1. The code is in
`src/analysis/differential.jl`, and the route in `src/server/routes/analysis.jl`.

Settings live under `analysis.differential` (see
[Configuring analysis](#configuring-analysis-analysis-in-pipelineyml)):

- `offset`: `tss` (default) uses each sample's total reads. `rle` uses the
median of ratios over the taxa with reads in every sample.
- `min_prevalence`: a fraction from 0 to 1 (default 0). A taxon with reads in a
smaller fraction of the samples is not fitted.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

The result is a volcano plot and a table. The plot draws log2 fold change
against -log10 of the raw p-value, coloured by whether the adjusted p-value is
below 0.05. Taxa without an adjusted p-value are left off the plot but stay in
the table. Each table row has a status:

- `ok`: the model fitted.
- `boundary`: the model fitted, but the dispersion parameter theta reached a
bound (at least 1e7, so effectively Poisson, or at most 1e-8). These taxa
keep their p-value and stay in the adjusted family.
- `failed`: no p-value, with the reason. Causes are counts that are constant
across samples, an error from `glm.nb`, a fit that did not converge, an
aliased group coefficient, or a non-finite estimate.
- `filtered`: below `min_prevalence`, so not fitted.

A failed or filtered taxon never gets a stand-in p-value, and it is not counted
in the adjustment. The table downloads as `differential_abundance.csv` and can
be added to the report. The CSV starts with `#` lines that record the
comparison, method and settings.

The test refuses, with an explicit error, when:

- the selection is not exactly two conditions, or both are the same;
- the two conditions resolve to different ranks;
- a condition has no results table, no taxonomy columns or no sample columns,
or the read-count bounds remove all its samples;
- `offset` or `min_prevalence` is invalid;
- a count is not a non-negative integer, a condition has no samples, or there
are fewer than 3 samples in total;
- `tss` meets a sample with no reads, or `rle` finds no taxon with reads in
every sample (use `tss`);
- the R package MASS cannot be loaded;
- no taxon could be fitted at all.

### Figures and report

The Figures tab lays analysis charts out on a page (A4, letter or a journal
Expand Down Expand Up @@ -554,13 +622,13 @@ Each entry names a filter in the `filters:` library of `config/composition.yml`.

### Configuring analysis (`analysis:` in `pipeline.yml`)

Controls the defaults applied to the analysis charts (alpha diversity, taxa bar, NMDS, etc.). Per-chart choices such as the taxonomic rank and relative or absolute abundance are made in the UI.
Controls the defaults applied to the analysis charts (alpha diversity, taxa bar, NMDS, differential abundance, etc.). Per-chart choices such as the taxonomic rank and relative or absolute abundance are made in the UI.

```yaml
analysis:
exclude_categories: # composition categories to drop from figures; [] to keep all
- {set: contamination, category: Contaminant, apply_to: [diversity, taxa, venn]}
# apply_to surfaces: diversity | taxa | composition | venn
- {set: contamination, category: Contaminant, apply_to: [diversity, taxa, venn, differential]}
# apply_to surfaces: diversity | taxa | composition | venn | differential
# (omit apply_to to act on every surface)
alpha: # richness, Shannon and Simpson
normalisation: none # none | rarefy | srs (scaling with ranked subsampling)
Expand All @@ -577,6 +645,9 @@ analysis:
depth: 0 # rarefy/srs reads per sample; 0 = smallest non-empty sample
nmds:
max_stress: 0.2 # warn if NMDS stress exceeds this value
differential: # negative-binomial differential abundance (MASS::glm.nb)
offset: tss # tss | rle (rle is refused when no taxon has reads in every sample)
min_prevalence: 0.0 # fraction of samples, 0 to 1, a taxon needs reads in to be tested
```

### Configuring taxonomic filtering (`filters:` in `config/composition.yml`)
Expand Down Expand Up @@ -762,7 +833,7 @@ The server exposes a REST API under `/api/v1/`. Key endpoint groups:
| Results | `GET/POST/DELETE .../results/tables/...` | List, query, filter, save, export (`.xlsx`), and delete tables; OTU member drill-down |
| QC | `GET .../results/qc`, `GET .../results/dada2` | MultiQC report metadata and DADA2 figures, logs, stats |
| Analysis | `POST .../runs/{run}/analysis/{alpha,chart}`, `GET .../runs/{run}/analysis/{pipeline-stats,ranks}` | Per-run charts and rank discovery |
| Cross-run | `POST /studies/{study}/analysis/{alpha,chart,chart-facet,nmds,permanova,venn,publication-tables}` | Comparison, faceted charts, NMDS, PERMANOVA, taxon overlap and publication tables across runs |
| Cross-run | `POST /studies/{study}/analysis/{alpha,chart,chart-facet,nmds,permanova,venn,publication-tables,differential}` | Comparison, faceted charts, NMDS, PERMANOVA, taxon overlap, publication tables and differential abundance across runs |
| Composition | `POST .../runs/{run}/composition/summary`, `POST .../composition/{source}/query`, `POST .../composition/{source}/distinct/{column}` | Organism-composition summaries and tables for a run |
| Composition library | `GET /composition`, `POST/DELETE /composition/{filters,sets}/{name}`, `GET /category-sets`, `POST/DELETE /category-sets/{name}` | Edit the filters and category sets in `config/composition.yml` |
| Reference trees | `GET/POST /reference-trees`, `GET/PUT/DELETE /reference-trees/{id}`, `GET/PUT .../fasta`, `POST .../run`, `GET .../log/{step}`, `GET .../qc/{align,trim}`, `GET .../alignment/{raw,trimmed}`, `GET .../treefile`, `POST .../trim-preview` | Build and inspect the shared reference trees |
Expand Down
Loading