allelix 2.1.0__tar.gz → 2.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {allelix-2.1.0 → allelix-2.1.1}/PKG-INFO +106 -8
- {allelix-2.1.0 → allelix-2.1.1}/README.md +105 -7
- {allelix-2.1.0 → allelix-2.1.1}/allelix/annotators/base.py +28 -19
- {allelix-2.1.0 → allelix-2.1.1}/allelix/models.py +36 -1
- {allelix-2.1.0 → allelix-2.1.1}/allelix/reports/diff.py +4 -2
- {allelix-2.1.0 → allelix-2.1.1}/allelix/reports/json_report.py +3 -5
- {allelix-2.1.0 → allelix-2.1.1}/allelix.egg-info/PKG-INFO +106 -8
- {allelix-2.1.0 → allelix-2.1.1}/pyproject.toml +1 -1
- {allelix-2.1.0 → allelix-2.1.1}/tests/test_models.py +46 -0
- {allelix-2.1.0 → allelix-2.1.1}/LICENSE +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/__init__.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/annotators/__init__.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/annotators/alphamissense.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/annotators/cadd.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/annotators/clinvar.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/annotators/gnomad.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/annotators/gwas.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/annotators/pharmgkb.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/annotators/snpedia.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/cli/__init__.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/cli/_helpers.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/cli/_options.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/cli/analyze.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/cli/config.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/cli/db.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/cli/focused.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/cli/utility.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/compare.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/config.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/data/__init__.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/data/clinvar_clnsig_snapshot.yaml +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/data/high_value_snps.yaml +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/__init__.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/_versions.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/alphamissense_loader.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/cadd_loader.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/cpic_loader.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/gnomad_loader.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/gwas_loader.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/loader_utils.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/manager.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/pharmgkb_loader.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/schema.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/snpedia_loader.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/databases/snpedia_parser.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/exporters/__init__.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/exporters/plink.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/__init__.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/_helpers.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/ancestrydna.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/base.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/ftdna.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/ftdna_illumina.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/livingdna.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/myhappygenes.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/myheritage.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/twentythreeandme.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/parsers/vcf.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/py.typed +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/reports/__init__.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/reports/_pipeline.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/reports/high_value.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/reports/html.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/reports/methylation.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/reports/terminal.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/utils/__init__.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/utils/allele.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix/utils/build_detect.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix.egg-info/SOURCES.txt +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix.egg-info/dependency_links.txt +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix.egg-info/entry_points.txt +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix.egg-info/requires.txt +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/allelix.egg-info/top_level.txt +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/setup.cfg +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/tests/test_cli.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/tests/test_cli_helpers.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/tests/test_compare.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/tests/test_config.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/tests/test_end_to_end.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/tests/test_mock_data_invariants.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/tests/test_registry.py +0 -0
- {allelix-2.1.0 → allelix-2.1.1}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: allelix
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Open-source genotype analysis toolkit. Format-agnostic ingestion, database-agnostic annotation, offline-first.
|
|
5
5
|
Author: Allelix
|
|
6
6
|
Maintainer-email: dial481 <dial481@users.noreply.github.com>
|
|
@@ -47,19 +47,26 @@ Open-source command-line toolkit for analyzing raw genotype files from consumer
|
|
|
47
47
|
> HTML/JSON/terminal reports, methylation + pharmacogenomics focused
|
|
48
48
|
> commands, report diffing, persistent config with commercial-mode
|
|
49
49
|
> safety switch. Build auto-detection from position data (ADR-0021).
|
|
50
|
-
> No regex on prose anywhere in production. **Latest: v2.
|
|
51
|
-
>
|
|
52
|
-
>
|
|
53
|
-
>
|
|
50
|
+
> No regex on prose anywhere in production. **Latest: v2.1.0** —
|
|
51
|
+
> ADR-0035 'Cluster B' coordinated model change: `Variant.ref` carries
|
|
52
|
+
> the forward-strand reference allele, per-Annotation `alt` threading
|
|
53
|
+
> on GWAS / SNPedia / ClinPGx enables allele-specific gnomAD /
|
|
54
|
+
> AlphaMissense / CADD enrichment on array inputs, structured GWAS
|
|
55
|
+
> fields (`trait` / `p_value` / `phecode`) replace prose re-parsing in
|
|
56
|
+
> the rollup, and ClinVar / ClinPGx carrier matching is strand-aware
|
|
57
|
+
> with multi-allelic safety guard. JSON `schema_version` bumped 4 → 5
|
|
58
|
+
> (additive, v4 readers unaffected).
|
|
54
59
|
> Release notes:
|
|
55
60
|
> [`CHANGELOG.md`](https://github.com/allelix/allelix/blob/main/CHANGELOG.md).
|
|
56
61
|
|
|
57
62
|
## Quickstart
|
|
58
63
|
|
|
64
|
+
**Requires Python 3.11 or newer.** On Python 3.10 or older, `pip install allelix` succeeds but the first command fails with `ImportError: cannot import name 'UTC' from 'datetime'` — that's the symptom; the fix is to install on 3.11+. Check with `python --version`.
|
|
65
|
+
|
|
59
66
|
```bash
|
|
60
67
|
pip install allelix
|
|
61
68
|
|
|
62
|
-
# Download reference databases (~
|
|
69
|
+
# Download reference databases (~16 GB default install; ~22 GB if you opt into CADD).
|
|
63
70
|
# Use --no-gnomad / --no-alphamissense to skip the large ones.
|
|
64
71
|
# CADD is opt-in: allelix db update --cadd
|
|
65
72
|
allelix db update
|
|
@@ -77,7 +84,7 @@ allelix analyze trio.vcf.gz --sample HG002 --output report.html
|
|
|
77
84
|
allelix analyze your_genotype_file.txt --filter-file my_panel.txt --output report.html
|
|
78
85
|
```
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
See [Development](#development) for source installs and running tests, [Managing your data](#managing-your-data) for cache locations and cleanup, and [Troubleshooting](#troubleshooting) for common failure states.
|
|
81
88
|
|
|
82
89
|
## Supported Formats
|
|
83
90
|
|
|
@@ -116,7 +123,20 @@ Adding a new format means adding one file to `allelix/parsers/` and registering
|
|
|
116
123
|
| GWAS Catalog | ✓ | Public domain (EBI/NHGRI). Trait–SNP associations with p-values and effect sizes. Carrier rule (ADR-0007) requires the user to carry the risk allele. P-value magnitude scoring (ADR-0024) maps continuous p-values to the 0–10 scale; unknown-risk-allele entries fire on rsID match alone but are capped at 3.0. |
|
|
117
124
|
| gnomAD | ✓ | ODbL v1.0. **Enrichment annotator** — adds population allele frequency context to existing annotations. Shows how common each variant is in the general population (~16M exome variants from 730K individuals). A pathogenic variant that 35% of people carry reads very differently from one seen in 0.001%. Pre-built cache downloaded via `db update` (~6GB on disk). Use `--no-gnomad` to skip. |
|
|
118
125
|
| AlphaMissense | ✓ | CC BY 4.0. **Enrichment annotator** — adds DeepMind's protein-structure-based pathogenicity predictions to existing annotations. Scores 71M missense variants on a 0–1 scale: <0.34 = likely benign, >0.564 = likely pathogenic. Complements ClinVar's expert classifications with computational predictions — especially valuable for variants ClinVar hasn't reviewed yet. Pre-built cache downloaded via `db update` (~8GB on disk). Use `--no-alphamissense` to skip. |
|
|
119
|
-
| CADD | ✓ | LicenseRef-CADD (non-commercial). **Enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7.
|
|
126
|
+
| CADD | ✓ | LicenseRef-CADD (non-commercial). **Enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7. **Opt-in**, disabled by default; see [CADD modes](#cadd-modes) below for details. |
|
|
127
|
+
|
|
128
|
+
### CADD modes
|
|
129
|
+
|
|
130
|
+
CADD ranks how deleterious any single-nucleotide variant is using 100+ annotation tracks (coding, non-coding, regulatory). PHRED 10 = top 10% most deleterious, 20 = top 1%, 30 = top 0.1%.
|
|
131
|
+
|
|
132
|
+
**Opt-in**: enable via `allelix db update --cadd` or `allelix config set sources.cadd true`. Use `--no-cadd` to skip enrichment for a single run.
|
|
133
|
+
|
|
134
|
+
Two modes:
|
|
135
|
+
|
|
136
|
+
- **Cache mode** (default): pre-built ~5.8 GB SQLite cache (~120M variant keys). Covers nearly every position allelix can annotate from its other databases (gnomAD, AlphaMissense, ClinVar). For genotyping chip data (23andMe, AncestryDNA, MyHappyGenes, etc.), this mode is functionally complete — chip probes overwhelmingly target known, cataloged variants.
|
|
137
|
+
- **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only). Adds coverage for novel or private variants that appear only in WGS/WES data and are not in any pre-computed database. Requires `pip install allelix[cadd]` for the pysam dependency.
|
|
138
|
+
|
|
139
|
+
If your input is a genotyping chip file, cache mode is all you need.
|
|
120
140
|
|
|
121
141
|
### Build coverage asymmetry (GRCh37 vs GRCh38)
|
|
122
142
|
|
|
@@ -214,6 +234,48 @@ gnomAD and AlphaMissense are the largest but add the most interpretive context.
|
|
|
214
234
|
|
|
215
235
|
To skip either during download: `allelix db update --no-gnomad --no-alphamissense`. To disable permanently: `allelix config set sources.gnomad false`.
|
|
216
236
|
|
|
237
|
+
### Managing your data
|
|
238
|
+
|
|
239
|
+
Allelix stores reference database caches at `~/.local/share/allelix/` (Linux/macOS) or the XDG-equivalent on other platforms. Default install (no CADD) is ~16 GB on disk; with the CADD opt-in it's ~22 GB.
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
~/.local/share/allelix/
|
|
243
|
+
├── alphamissense.sqlite ~7.8 GB
|
|
244
|
+
├── gnomad.sqlite ~6.1 GB
|
|
245
|
+
├── cadd.sqlite ~5.8 GB (only if you opted into --cadd)
|
|
246
|
+
├── clinvar.GRCh37.sqlite ~470 MB
|
|
247
|
+
├── clinvar.GRCh38.sqlite ~480 MB
|
|
248
|
+
├── gwas_catalog_associations.tsv ~700 MB
|
|
249
|
+
├── snpedia.sqlite ~390 MB
|
|
250
|
+
├── gwas.sqlite ~190 MB
|
|
251
|
+
└── pharmgkb.sqlite ~14 MB
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**The caches are disposable and re-downloadable.** Everything here was fetched by `allelix db update` and can be re-fetched at any time. There's no per-user state in the cache — your genotype files never enter this directory. Safe to delete to reclaim space (or to force a full refresh if you suspect a cache went stale):
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Reclaim all ~16-22 GB
|
|
258
|
+
rm -rf ~/.local/share/allelix/
|
|
259
|
+
|
|
260
|
+
# Reclaim everything except CADD (avoids re-downloading the slowest one)
|
|
261
|
+
find ~/.local/share/allelix/ -mindepth 1 ! -name 'cadd*.sqlite*' -delete
|
|
262
|
+
|
|
263
|
+
# Re-populate from scratch next time you analyze a file
|
|
264
|
+
allelix db update
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**The one thing worth backing up: `config.toml`.** It lives separately (XDG config dir, typically `~/.config/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings. It's the only non-reproducible state in the install. The caches will rebuild on the next `db update`; `config.toml` won't.
|
|
268
|
+
|
|
269
|
+
To fully uninstall allelix and reclaim everything:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
pip uninstall allelix
|
|
273
|
+
rm -rf ~/.local/share/allelix/ # reference database caches
|
|
274
|
+
rm -rf ~/.config/allelix/ # license + source-toggle config
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
A `allelix db clean` subcommand to automate the cache-clear step (with confirmation prompt and size report) is planned for v2.2 alongside `allelix db path` (prints the resolved data directory for scripting / backup integration).
|
|
278
|
+
|
|
217
279
|
## Data Sources & Licensing
|
|
218
280
|
|
|
219
281
|
Allelix source code is licensed under the **GNU Affero General Public License v3.0 or later** (AGPL-3.0-or-later). Allelix ships with **zero third-party data**. All reference databases are downloaded by the user at runtime via `allelix db update`. Each database retains its original license on the user's machine:
|
|
@@ -256,6 +318,42 @@ Of the 104,806 genotype pages in the archive:
|
|
|
256
318
|
|
|
257
319
|
None of these are scraping errors. They are editorial inconsistencies on the source wiki. The annotator handles all of them correctly: incomplete entries are skipped, variant title formats are matched, and no false annotations are produced.
|
|
258
320
|
|
|
321
|
+
## Troubleshooting
|
|
322
|
+
|
|
323
|
+
### `ImportError: cannot import name 'UTC' from 'datetime'`
|
|
324
|
+
|
|
325
|
+
You're on Python 3.10 or older. Allelix requires 3.11+. `python --version` to check; upgrade and reinstall.
|
|
326
|
+
|
|
327
|
+
### `allelix db update` failed partway through
|
|
328
|
+
|
|
329
|
+
Run it again. The update is idempotent — completed downloads are skipped (signal-matched against the upstream freshness probe), so re-running picks up where it left off. If a specific source keeps failing, you can isolate it: `allelix db update --no-gnomad` (or `--no-alphamissense` etc.) to skip the failing source for now, then retry that source standalone later.
|
|
330
|
+
|
|
331
|
+
If the failure is a network timeout on the same source repeatedly, the issue is likely on the source's end (HuggingFace / NCBI / EBI / etc. transient outage). Wait an hour and retry.
|
|
332
|
+
|
|
333
|
+
### `db status` shows an annotator as "not ready" but the cache files exist on disk
|
|
334
|
+
|
|
335
|
+
Almost always a schema or interpreter version bump in a recent allelix release that invalidates the pre-bump cache. The fix is `allelix db update` — it'll re-ingest only the affected annotators (everything else signal-skips). See the relevant `CHANGELOG.md` entry for "Cache invalidation" notes per release.
|
|
336
|
+
|
|
337
|
+
### Build mismatch warning when analyzing a known-good file
|
|
338
|
+
|
|
339
|
+
Either: (a) the file's header claims one build and the position data is actually a different build (real-world MHG / Tempus exports do this — header says 37, positions are 38; ADR-0021 documents the auto-detection), or (b) the file is a GRCh36-era export (pre-2012), which allelix detects accurately but doesn't ship a ClinVar cache for (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md) for the external-liftover path).
|
|
340
|
+
|
|
341
|
+
If you trust the file's header more than allelix's detection, force the build: `--build grch37` or `--build grch38`. The build banner in the analyze output will reflect what was actually used.
|
|
342
|
+
|
|
343
|
+
### Is my database stale? How fresh is the data?
|
|
344
|
+
|
|
345
|
+
`allelix db status` shows the ingestion date of each cache. The freshness model is signal-driven: `allelix db update` queries an upstream freshness signal (md5 / etag) per source and re-ingests only when the signal changed. There's no time-based staleness — a 6-month-old cache against an unchanged source IS current.
|
|
346
|
+
|
|
347
|
+
If you want to force a refresh: `allelix db update --force` re-downloads every cache regardless of freshness signal.
|
|
348
|
+
|
|
349
|
+
### Output reports contain real genome annotations
|
|
350
|
+
|
|
351
|
+
Anything `allelix analyze` writes to disk contains real annotations of your genome. Treat the output files (HTML, JSON, terminal redirects) as sensitive — they're as private as the input genotype file. Allelix does not transmit them; the lifecycle is local.
|
|
352
|
+
|
|
353
|
+
### Where does allelix store its data?
|
|
354
|
+
|
|
355
|
+
See [Managing your data](#managing-your-data) above for cache locations, the disposability rule, what to back up, and the uninstall procedure.
|
|
356
|
+
|
|
259
357
|
## Architecture & Design Decisions
|
|
260
358
|
|
|
261
359
|
The "why" behind major design choices lives in [`docs/adr/`](https://github.com/allelix/allelix/blob/main/docs/adr/README.md) as Architecture Decision Records. Read these before proposing changes that touch the parser/annotator interfaces, the regulatory posture, or the data-handling model.
|
|
@@ -10,19 +10,26 @@ Open-source command-line toolkit for analyzing raw genotype files from consumer
|
|
|
10
10
|
> HTML/JSON/terminal reports, methylation + pharmacogenomics focused
|
|
11
11
|
> commands, report diffing, persistent config with commercial-mode
|
|
12
12
|
> safety switch. Build auto-detection from position data (ADR-0021).
|
|
13
|
-
> No regex on prose anywhere in production. **Latest: v2.
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
13
|
+
> No regex on prose anywhere in production. **Latest: v2.1.0** —
|
|
14
|
+
> ADR-0035 'Cluster B' coordinated model change: `Variant.ref` carries
|
|
15
|
+
> the forward-strand reference allele, per-Annotation `alt` threading
|
|
16
|
+
> on GWAS / SNPedia / ClinPGx enables allele-specific gnomAD /
|
|
17
|
+
> AlphaMissense / CADD enrichment on array inputs, structured GWAS
|
|
18
|
+
> fields (`trait` / `p_value` / `phecode`) replace prose re-parsing in
|
|
19
|
+
> the rollup, and ClinVar / ClinPGx carrier matching is strand-aware
|
|
20
|
+
> with multi-allelic safety guard. JSON `schema_version` bumped 4 → 5
|
|
21
|
+
> (additive, v4 readers unaffected).
|
|
17
22
|
> Release notes:
|
|
18
23
|
> [`CHANGELOG.md`](https://github.com/allelix/allelix/blob/main/CHANGELOG.md).
|
|
19
24
|
|
|
20
25
|
## Quickstart
|
|
21
26
|
|
|
27
|
+
**Requires Python 3.11 or newer.** On Python 3.10 or older, `pip install allelix` succeeds but the first command fails with `ImportError: cannot import name 'UTC' from 'datetime'` — that's the symptom; the fix is to install on 3.11+. Check with `python --version`.
|
|
28
|
+
|
|
22
29
|
```bash
|
|
23
30
|
pip install allelix
|
|
24
31
|
|
|
25
|
-
# Download reference databases (~
|
|
32
|
+
# Download reference databases (~16 GB default install; ~22 GB if you opt into CADD).
|
|
26
33
|
# Use --no-gnomad / --no-alphamissense to skip the large ones.
|
|
27
34
|
# CADD is opt-in: allelix db update --cadd
|
|
28
35
|
allelix db update
|
|
@@ -40,7 +47,7 @@ allelix analyze trio.vcf.gz --sample HG002 --output report.html
|
|
|
40
47
|
allelix analyze your_genotype_file.txt --filter-file my_panel.txt --output report.html
|
|
41
48
|
```
|
|
42
49
|
|
|
43
|
-
|
|
50
|
+
See [Development](#development) for source installs and running tests, [Managing your data](#managing-your-data) for cache locations and cleanup, and [Troubleshooting](#troubleshooting) for common failure states.
|
|
44
51
|
|
|
45
52
|
## Supported Formats
|
|
46
53
|
|
|
@@ -79,7 +86,20 @@ Adding a new format means adding one file to `allelix/parsers/` and registering
|
|
|
79
86
|
| GWAS Catalog | ✓ | Public domain (EBI/NHGRI). Trait–SNP associations with p-values and effect sizes. Carrier rule (ADR-0007) requires the user to carry the risk allele. P-value magnitude scoring (ADR-0024) maps continuous p-values to the 0–10 scale; unknown-risk-allele entries fire on rsID match alone but are capped at 3.0. |
|
|
80
87
|
| gnomAD | ✓ | ODbL v1.0. **Enrichment annotator** — adds population allele frequency context to existing annotations. Shows how common each variant is in the general population (~16M exome variants from 730K individuals). A pathogenic variant that 35% of people carry reads very differently from one seen in 0.001%. Pre-built cache downloaded via `db update` (~6GB on disk). Use `--no-gnomad` to skip. |
|
|
81
88
|
| AlphaMissense | ✓ | CC BY 4.0. **Enrichment annotator** — adds DeepMind's protein-structure-based pathogenicity predictions to existing annotations. Scores 71M missense variants on a 0–1 scale: <0.34 = likely benign, >0.564 = likely pathogenic. Complements ClinVar's expert classifications with computational predictions — especially valuable for variants ClinVar hasn't reviewed yet. Pre-built cache downloaded via `db update` (~8GB on disk). Use `--no-alphamissense` to skip. |
|
|
82
|
-
| CADD | ✓ | LicenseRef-CADD (non-commercial). **Enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7.
|
|
89
|
+
| CADD | ✓ | LicenseRef-CADD (non-commercial). **Enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7. **Opt-in**, disabled by default; see [CADD modes](#cadd-modes) below for details. |
|
|
90
|
+
|
|
91
|
+
### CADD modes
|
|
92
|
+
|
|
93
|
+
CADD ranks how deleterious any single-nucleotide variant is using 100+ annotation tracks (coding, non-coding, regulatory). PHRED 10 = top 10% most deleterious, 20 = top 1%, 30 = top 0.1%.
|
|
94
|
+
|
|
95
|
+
**Opt-in**: enable via `allelix db update --cadd` or `allelix config set sources.cadd true`. Use `--no-cadd` to skip enrichment for a single run.
|
|
96
|
+
|
|
97
|
+
Two modes:
|
|
98
|
+
|
|
99
|
+
- **Cache mode** (default): pre-built ~5.8 GB SQLite cache (~120M variant keys). Covers nearly every position allelix can annotate from its other databases (gnomAD, AlphaMissense, ClinVar). For genotyping chip data (23andMe, AncestryDNA, MyHappyGenes, etc.), this mode is functionally complete — chip probes overwhelmingly target known, cataloged variants.
|
|
100
|
+
- **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only). Adds coverage for novel or private variants that appear only in WGS/WES data and are not in any pre-computed database. Requires `pip install allelix[cadd]` for the pysam dependency.
|
|
101
|
+
|
|
102
|
+
If your input is a genotyping chip file, cache mode is all you need.
|
|
83
103
|
|
|
84
104
|
### Build coverage asymmetry (GRCh37 vs GRCh38)
|
|
85
105
|
|
|
@@ -177,6 +197,48 @@ gnomAD and AlphaMissense are the largest but add the most interpretive context.
|
|
|
177
197
|
|
|
178
198
|
To skip either during download: `allelix db update --no-gnomad --no-alphamissense`. To disable permanently: `allelix config set sources.gnomad false`.
|
|
179
199
|
|
|
200
|
+
### Managing your data
|
|
201
|
+
|
|
202
|
+
Allelix stores reference database caches at `~/.local/share/allelix/` (Linux/macOS) or the XDG-equivalent on other platforms. Default install (no CADD) is ~16 GB on disk; with the CADD opt-in it's ~22 GB.
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
~/.local/share/allelix/
|
|
206
|
+
├── alphamissense.sqlite ~7.8 GB
|
|
207
|
+
├── gnomad.sqlite ~6.1 GB
|
|
208
|
+
├── cadd.sqlite ~5.8 GB (only if you opted into --cadd)
|
|
209
|
+
├── clinvar.GRCh37.sqlite ~470 MB
|
|
210
|
+
├── clinvar.GRCh38.sqlite ~480 MB
|
|
211
|
+
├── gwas_catalog_associations.tsv ~700 MB
|
|
212
|
+
├── snpedia.sqlite ~390 MB
|
|
213
|
+
├── gwas.sqlite ~190 MB
|
|
214
|
+
└── pharmgkb.sqlite ~14 MB
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**The caches are disposable and re-downloadable.** Everything here was fetched by `allelix db update` and can be re-fetched at any time. There's no per-user state in the cache — your genotype files never enter this directory. Safe to delete to reclaim space (or to force a full refresh if you suspect a cache went stale):
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# Reclaim all ~16-22 GB
|
|
221
|
+
rm -rf ~/.local/share/allelix/
|
|
222
|
+
|
|
223
|
+
# Reclaim everything except CADD (avoids re-downloading the slowest one)
|
|
224
|
+
find ~/.local/share/allelix/ -mindepth 1 ! -name 'cadd*.sqlite*' -delete
|
|
225
|
+
|
|
226
|
+
# Re-populate from scratch next time you analyze a file
|
|
227
|
+
allelix db update
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**The one thing worth backing up: `config.toml`.** It lives separately (XDG config dir, typically `~/.config/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings. It's the only non-reproducible state in the install. The caches will rebuild on the next `db update`; `config.toml` won't.
|
|
231
|
+
|
|
232
|
+
To fully uninstall allelix and reclaim everything:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
pip uninstall allelix
|
|
236
|
+
rm -rf ~/.local/share/allelix/ # reference database caches
|
|
237
|
+
rm -rf ~/.config/allelix/ # license + source-toggle config
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
A `allelix db clean` subcommand to automate the cache-clear step (with confirmation prompt and size report) is planned for v2.2 alongside `allelix db path` (prints the resolved data directory for scripting / backup integration).
|
|
241
|
+
|
|
180
242
|
## Data Sources & Licensing
|
|
181
243
|
|
|
182
244
|
Allelix source code is licensed under the **GNU Affero General Public License v3.0 or later** (AGPL-3.0-or-later). Allelix ships with **zero third-party data**. All reference databases are downloaded by the user at runtime via `allelix db update`. Each database retains its original license on the user's machine:
|
|
@@ -219,6 +281,42 @@ Of the 104,806 genotype pages in the archive:
|
|
|
219
281
|
|
|
220
282
|
None of these are scraping errors. They are editorial inconsistencies on the source wiki. The annotator handles all of them correctly: incomplete entries are skipped, variant title formats are matched, and no false annotations are produced.
|
|
221
283
|
|
|
284
|
+
## Troubleshooting
|
|
285
|
+
|
|
286
|
+
### `ImportError: cannot import name 'UTC' from 'datetime'`
|
|
287
|
+
|
|
288
|
+
You're on Python 3.10 or older. Allelix requires 3.11+. `python --version` to check; upgrade and reinstall.
|
|
289
|
+
|
|
290
|
+
### `allelix db update` failed partway through
|
|
291
|
+
|
|
292
|
+
Run it again. The update is idempotent — completed downloads are skipped (signal-matched against the upstream freshness probe), so re-running picks up where it left off. If a specific source keeps failing, you can isolate it: `allelix db update --no-gnomad` (or `--no-alphamissense` etc.) to skip the failing source for now, then retry that source standalone later.
|
|
293
|
+
|
|
294
|
+
If the failure is a network timeout on the same source repeatedly, the issue is likely on the source's end (HuggingFace / NCBI / EBI / etc. transient outage). Wait an hour and retry.
|
|
295
|
+
|
|
296
|
+
### `db status` shows an annotator as "not ready" but the cache files exist on disk
|
|
297
|
+
|
|
298
|
+
Almost always a schema or interpreter version bump in a recent allelix release that invalidates the pre-bump cache. The fix is `allelix db update` — it'll re-ingest only the affected annotators (everything else signal-skips). See the relevant `CHANGELOG.md` entry for "Cache invalidation" notes per release.
|
|
299
|
+
|
|
300
|
+
### Build mismatch warning when analyzing a known-good file
|
|
301
|
+
|
|
302
|
+
Either: (a) the file's header claims one build and the position data is actually a different build (real-world MHG / Tempus exports do this — header says 37, positions are 38; ADR-0021 documents the auto-detection), or (b) the file is a GRCh36-era export (pre-2012), which allelix detects accurately but doesn't ship a ClinVar cache for (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md) for the external-liftover path).
|
|
303
|
+
|
|
304
|
+
If you trust the file's header more than allelix's detection, force the build: `--build grch37` or `--build grch38`. The build banner in the analyze output will reflect what was actually used.
|
|
305
|
+
|
|
306
|
+
### Is my database stale? How fresh is the data?
|
|
307
|
+
|
|
308
|
+
`allelix db status` shows the ingestion date of each cache. The freshness model is signal-driven: `allelix db update` queries an upstream freshness signal (md5 / etag) per source and re-ingests only when the signal changed. There's no time-based staleness — a 6-month-old cache against an unchanged source IS current.
|
|
309
|
+
|
|
310
|
+
If you want to force a refresh: `allelix db update --force` re-downloads every cache regardless of freshness signal.
|
|
311
|
+
|
|
312
|
+
### Output reports contain real genome annotations
|
|
313
|
+
|
|
314
|
+
Anything `allelix analyze` writes to disk contains real annotations of your genome. Treat the output files (HTML, JSON, terminal redirects) as sensitive — they're as private as the input genotype file. Allelix does not transmit them; the lifecycle is local.
|
|
315
|
+
|
|
316
|
+
### Where does allelix store its data?
|
|
317
|
+
|
|
318
|
+
See [Managing your data](#managing-your-data) above for cache locations, the disposability rule, what to back up, and the uninstall procedure.
|
|
319
|
+
|
|
222
320
|
## Architecture & Design Decisions
|
|
223
321
|
|
|
224
322
|
The "why" behind major design choices lives in [`docs/adr/`](https://github.com/allelix/allelix/blob/main/docs/adr/README.md) as Architecture Decision Records. Read these before proposing changes that touch the parser/annotator interfaces, the regulatory posture, or the data-handling model.
|
|
@@ -144,27 +144,36 @@ class Annotator(ABC):
|
|
|
144
144
|
def __del__(self) -> None:
|
|
145
145
|
"""Safety-net resource release on GC. Deliberately retained.
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
147
|
+
Status (v2.1.1 audit, GH #36):
|
|
148
|
+
- **Production callsites are clean.** ``run_analysis`` wires every
|
|
149
|
+
primary + enrichment annotator into ``contextlib.ExitStack``
|
|
150
|
+
(v2.0.2 fix). ``allelix/cli/utility.py`` lines 221 and 476 each
|
|
151
|
+
construct a ``GnomadAnnotator`` inside ``try / finally
|
|
152
|
+
gnomad.close()``. ``allelix/annotators/__init__.py`` returns a
|
|
153
|
+
list that the pipeline immediately wraps in ``ExitStack``.
|
|
154
|
+
- **Test callsites NOT yet clean.** ~27 sites in
|
|
155
|
+
``tests/reports/test_pipeline.py`` instantiate annotators
|
|
156
|
+
directly without a context manager or explicit close. The
|
|
157
|
+
pipeline closes them via its own ``ExitStack`` during
|
|
158
|
+
``run_analysis``, but the test still holds the closed-instance
|
|
159
|
+
reference, and the underlying sqlite3 ``Connection``'s GC
|
|
160
|
+
finalizer surfaces a ``ResourceWarning`` during the next test's
|
|
161
|
+
startup. pytest's ``error::PytestUnraisableExceptionWarning``
|
|
162
|
+
filter then converts it to a hard failure on the gate.
|
|
163
|
+
|
|
164
|
+
Removing ``__del__`` without first refactoring the test sites
|
|
165
|
+
therefore fails the gate (verified — 3 failures, all in
|
|
166
|
+
``tests/reports/test_pipeline.py``, triggered by leaks from
|
|
167
|
+
earlier tests in the same file). The test-site refactor is
|
|
168
|
+
tracked separately; ``__del__`` stays as the safety net until
|
|
169
|
+
that lands. v2.1.x task — see the follow-up issue filed at
|
|
170
|
+
``#36``-closure-time.
|
|
162
171
|
|
|
163
172
|
``contextlib.suppress(Exception)`` is deliberate — ``__del__``
|
|
164
|
-
must never raise.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
173
|
+
must never raise. GC timing and shutdown-ordering edges are
|
|
174
|
+
explicitly silenced; this is exactly the "if you must keep
|
|
175
|
+
``__del__``, make absolutely sure it can never raise"
|
|
176
|
+
mitigation the audit recommended.
|
|
168
177
|
"""
|
|
169
178
|
with contextlib.suppress(Exception):
|
|
170
179
|
self.close()
|
|
@@ -11,7 +11,7 @@ the validation.
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
-
from dataclasses import dataclass, field
|
|
14
|
+
from dataclasses import asdict, dataclass, field
|
|
15
15
|
|
|
16
16
|
NO_CALL_MARKER = "-"
|
|
17
17
|
DEFAULT_BUILD = "GRCh37"
|
|
@@ -138,6 +138,10 @@ class Annotation:
|
|
|
138
138
|
gene: str = ""
|
|
139
139
|
review_status: str = ""
|
|
140
140
|
alt: str = ""
|
|
141
|
+
# Internal flag — see ``_INTERNAL_ANNOTATION_FIELDS`` below. Excluded
|
|
142
|
+
# from every public serialization path (JSON report, diff entries,
|
|
143
|
+
# diff change records). If you add another internal-only field,
|
|
144
|
+
# extend the frozenset; the three call sites all read from it.
|
|
141
145
|
is_must_include: bool = False
|
|
142
146
|
allele_frequency: float | None = None
|
|
143
147
|
am_pathogenicity: float | None = None
|
|
@@ -162,3 +166,34 @@ class Annotation:
|
|
|
162
166
|
if len(parts) != 2:
|
|
163
167
|
return "Homozygous" if len(set(self.genotype_match)) == 1 else "Heterozygous"
|
|
164
168
|
return "Heterozygous" if parts[0] != parts[1] else "Homozygous"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# Annotation fields that are internal-only and MUST be stripped from every
|
|
172
|
+
# public serialization path (JSON report annotations, JSON diff entries,
|
|
173
|
+
# diff change records). Adding a new internal field? Extend this frozenset
|
|
174
|
+
# and every existing serializer picks up the exclusion automatically — no
|
|
175
|
+
# new "remember to strip the field" obligation across N call sites.
|
|
176
|
+
#
|
|
177
|
+
# GH allelix-dev #3 (the v2.1.1 "is_must_include leak by omission risk"
|
|
178
|
+
# cleanup) eliminated the three duplicated inline filters that previously
|
|
179
|
+
# carried this rule. If you find yourself writing
|
|
180
|
+
# ``{k: v for k, v in asdict(a).items() if k != "<some_field>"}`` in a
|
|
181
|
+
# new public-output path, you're re-introducing the bug — add the field
|
|
182
|
+
# here and use ``annotation_to_public_dict`` instead.
|
|
183
|
+
_INTERNAL_ANNOTATION_FIELDS: frozenset[str] = frozenset({"is_must_include"})
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def annotation_to_public_dict(a: Annotation) -> dict:
|
|
187
|
+
"""Serialize an Annotation for public output, stripping internal fields.
|
|
188
|
+
|
|
189
|
+
Canonical helper for every public Annotation serialization path. The
|
|
190
|
+
JSON report's main annotation list, JSON diff entries, and diff change
|
|
191
|
+
records all route through this — so any future internal-only field
|
|
192
|
+
just needs adding to ``_INTERNAL_ANNOTATION_FIELDS`` and every consumer
|
|
193
|
+
inherits the exclusion.
|
|
194
|
+
|
|
195
|
+
Does NOT add display-derived fields (``zygosity``, ``am_caveat``,
|
|
196
|
+
diff-specific ``previous_*``) — those are layered by the calling
|
|
197
|
+
serializer after this helper has stripped internal state.
|
|
198
|
+
"""
|
|
199
|
+
return {k: v for k, v in asdict(a).items() if k not in _INTERNAL_ANNOTATION_FIELDS}
|
|
@@ -18,9 +18,11 @@ from __future__ import annotations
|
|
|
18
18
|
import json
|
|
19
19
|
import math
|
|
20
20
|
from collections import Counter
|
|
21
|
-
from dataclasses import
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
22
|
from typing import TYPE_CHECKING
|
|
23
23
|
|
|
24
|
+
from allelix.models import annotation_to_public_dict
|
|
25
|
+
|
|
24
26
|
if TYPE_CHECKING:
|
|
25
27
|
from pathlib import Path
|
|
26
28
|
|
|
@@ -202,7 +204,7 @@ def summarize_diff(diff: DiffResult) -> str:
|
|
|
202
204
|
|
|
203
205
|
def diff_annotation_to_dict(a: ChangedAnnotation) -> dict:
|
|
204
206
|
"""Serialize a ChangedAnnotation for JSON output."""
|
|
205
|
-
d =
|
|
207
|
+
d = annotation_to_public_dict(a.current)
|
|
206
208
|
d["previous_significance"] = a.previous_significance
|
|
207
209
|
d["previous_magnitude"] = a.previous_magnitude
|
|
208
210
|
return d
|
|
@@ -35,11 +35,11 @@ or strips that field.
|
|
|
35
35
|
from __future__ import annotations
|
|
36
36
|
|
|
37
37
|
import json
|
|
38
|
-
from dataclasses import asdict
|
|
39
38
|
from datetime import UTC, datetime
|
|
40
39
|
from typing import TYPE_CHECKING
|
|
41
40
|
|
|
42
41
|
from allelix import __version__
|
|
42
|
+
from allelix.models import annotation_to_public_dict
|
|
43
43
|
from allelix.reports import REGULATORY_NOTICE, atomic_write_text
|
|
44
44
|
from allelix.reports._pipeline import rollup_gwas_duplicates
|
|
45
45
|
|
|
@@ -61,7 +61,7 @@ SCHEMA_VERSION = "5"
|
|
|
61
61
|
|
|
62
62
|
def _annotation_dict(a: Annotation) -> dict:
|
|
63
63
|
"""Serialize an annotation, adding AM caveat for non-protein sources."""
|
|
64
|
-
d =
|
|
64
|
+
d = annotation_to_public_dict(a)
|
|
65
65
|
d["zygosity"] = a.zygosity
|
|
66
66
|
if a.am_pathogenicity is not None and a.source == "pharmgkb":
|
|
67
67
|
d["am_caveat"] = "protein structure impact only"
|
|
@@ -159,9 +159,7 @@ def render_json(
|
|
|
159
159
|
payload["diff"] = {
|
|
160
160
|
"previous_report": diff.previous_generated_at,
|
|
161
161
|
"summary": summarize_diff(diff),
|
|
162
|
-
"new": [
|
|
163
|
-
{k: v for k, v in asdict(a).items() if k != "is_must_include"} for a in diff.new
|
|
164
|
-
],
|
|
162
|
+
"new": [annotation_to_public_dict(a) for a in diff.new],
|
|
165
163
|
"changed": [diff_annotation_to_dict(c) for c in diff.changed],
|
|
166
164
|
"removed": diff.removed,
|
|
167
165
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: allelix
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Open-source genotype analysis toolkit. Format-agnostic ingestion, database-agnostic annotation, offline-first.
|
|
5
5
|
Author: Allelix
|
|
6
6
|
Maintainer-email: dial481 <dial481@users.noreply.github.com>
|
|
@@ -47,19 +47,26 @@ Open-source command-line toolkit for analyzing raw genotype files from consumer
|
|
|
47
47
|
> HTML/JSON/terminal reports, methylation + pharmacogenomics focused
|
|
48
48
|
> commands, report diffing, persistent config with commercial-mode
|
|
49
49
|
> safety switch. Build auto-detection from position data (ADR-0021).
|
|
50
|
-
> No regex on prose anywhere in production. **Latest: v2.
|
|
51
|
-
>
|
|
52
|
-
>
|
|
53
|
-
>
|
|
50
|
+
> No regex on prose anywhere in production. **Latest: v2.1.0** —
|
|
51
|
+
> ADR-0035 'Cluster B' coordinated model change: `Variant.ref` carries
|
|
52
|
+
> the forward-strand reference allele, per-Annotation `alt` threading
|
|
53
|
+
> on GWAS / SNPedia / ClinPGx enables allele-specific gnomAD /
|
|
54
|
+
> AlphaMissense / CADD enrichment on array inputs, structured GWAS
|
|
55
|
+
> fields (`trait` / `p_value` / `phecode`) replace prose re-parsing in
|
|
56
|
+
> the rollup, and ClinVar / ClinPGx carrier matching is strand-aware
|
|
57
|
+
> with multi-allelic safety guard. JSON `schema_version` bumped 4 → 5
|
|
58
|
+
> (additive, v4 readers unaffected).
|
|
54
59
|
> Release notes:
|
|
55
60
|
> [`CHANGELOG.md`](https://github.com/allelix/allelix/blob/main/CHANGELOG.md).
|
|
56
61
|
|
|
57
62
|
## Quickstart
|
|
58
63
|
|
|
64
|
+
**Requires Python 3.11 or newer.** On Python 3.10 or older, `pip install allelix` succeeds but the first command fails with `ImportError: cannot import name 'UTC' from 'datetime'` — that's the symptom; the fix is to install on 3.11+. Check with `python --version`.
|
|
65
|
+
|
|
59
66
|
```bash
|
|
60
67
|
pip install allelix
|
|
61
68
|
|
|
62
|
-
# Download reference databases (~
|
|
69
|
+
# Download reference databases (~16 GB default install; ~22 GB if you opt into CADD).
|
|
63
70
|
# Use --no-gnomad / --no-alphamissense to skip the large ones.
|
|
64
71
|
# CADD is opt-in: allelix db update --cadd
|
|
65
72
|
allelix db update
|
|
@@ -77,7 +84,7 @@ allelix analyze trio.vcf.gz --sample HG002 --output report.html
|
|
|
77
84
|
allelix analyze your_genotype_file.txt --filter-file my_panel.txt --output report.html
|
|
78
85
|
```
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
See [Development](#development) for source installs and running tests, [Managing your data](#managing-your-data) for cache locations and cleanup, and [Troubleshooting](#troubleshooting) for common failure states.
|
|
81
88
|
|
|
82
89
|
## Supported Formats
|
|
83
90
|
|
|
@@ -116,7 +123,20 @@ Adding a new format means adding one file to `allelix/parsers/` and registering
|
|
|
116
123
|
| GWAS Catalog | ✓ | Public domain (EBI/NHGRI). Trait–SNP associations with p-values and effect sizes. Carrier rule (ADR-0007) requires the user to carry the risk allele. P-value magnitude scoring (ADR-0024) maps continuous p-values to the 0–10 scale; unknown-risk-allele entries fire on rsID match alone but are capped at 3.0. |
|
|
117
124
|
| gnomAD | ✓ | ODbL v1.0. **Enrichment annotator** — adds population allele frequency context to existing annotations. Shows how common each variant is in the general population (~16M exome variants from 730K individuals). A pathogenic variant that 35% of people carry reads very differently from one seen in 0.001%. Pre-built cache downloaded via `db update` (~6GB on disk). Use `--no-gnomad` to skip. |
|
|
118
125
|
| AlphaMissense | ✓ | CC BY 4.0. **Enrichment annotator** — adds DeepMind's protein-structure-based pathogenicity predictions to existing annotations. Scores 71M missense variants on a 0–1 scale: <0.34 = likely benign, >0.564 = likely pathogenic. Complements ClinVar's expert classifications with computational predictions — especially valuable for variants ClinVar hasn't reviewed yet. Pre-built cache downloaded via `db update` (~8GB on disk). Use `--no-alphamissense` to skip. |
|
|
119
|
-
| CADD | ✓ | LicenseRef-CADD (non-commercial). **Enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7.
|
|
126
|
+
| CADD | ✓ | LicenseRef-CADD (non-commercial). **Enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7. **Opt-in**, disabled by default; see [CADD modes](#cadd-modes) below for details. |
|
|
127
|
+
|
|
128
|
+
### CADD modes
|
|
129
|
+
|
|
130
|
+
CADD ranks how deleterious any single-nucleotide variant is using 100+ annotation tracks (coding, non-coding, regulatory). PHRED 10 = top 10% most deleterious, 20 = top 1%, 30 = top 0.1%.
|
|
131
|
+
|
|
132
|
+
**Opt-in**: enable via `allelix db update --cadd` or `allelix config set sources.cadd true`. Use `--no-cadd` to skip enrichment for a single run.
|
|
133
|
+
|
|
134
|
+
Two modes:
|
|
135
|
+
|
|
136
|
+
- **Cache mode** (default): pre-built ~5.8 GB SQLite cache (~120M variant keys). Covers nearly every position allelix can annotate from its other databases (gnomAD, AlphaMissense, ClinVar). For genotyping chip data (23andMe, AncestryDNA, MyHappyGenes, etc.), this mode is functionally complete — chip probes overwhelmingly target known, cataloged variants.
|
|
137
|
+
- **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only). Adds coverage for novel or private variants that appear only in WGS/WES data and are not in any pre-computed database. Requires `pip install allelix[cadd]` for the pysam dependency.
|
|
138
|
+
|
|
139
|
+
If your input is a genotyping chip file, cache mode is all you need.
|
|
120
140
|
|
|
121
141
|
### Build coverage asymmetry (GRCh37 vs GRCh38)
|
|
122
142
|
|
|
@@ -214,6 +234,48 @@ gnomAD and AlphaMissense are the largest but add the most interpretive context.
|
|
|
214
234
|
|
|
215
235
|
To skip either during download: `allelix db update --no-gnomad --no-alphamissense`. To disable permanently: `allelix config set sources.gnomad false`.
|
|
216
236
|
|
|
237
|
+
### Managing your data
|
|
238
|
+
|
|
239
|
+
Allelix stores reference database caches at `~/.local/share/allelix/` (Linux/macOS) or the XDG-equivalent on other platforms. Default install (no CADD) is ~16 GB on disk; with the CADD opt-in it's ~22 GB.
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
~/.local/share/allelix/
|
|
243
|
+
├── alphamissense.sqlite ~7.8 GB
|
|
244
|
+
├── gnomad.sqlite ~6.1 GB
|
|
245
|
+
├── cadd.sqlite ~5.8 GB (only if you opted into --cadd)
|
|
246
|
+
├── clinvar.GRCh37.sqlite ~470 MB
|
|
247
|
+
├── clinvar.GRCh38.sqlite ~480 MB
|
|
248
|
+
├── gwas_catalog_associations.tsv ~700 MB
|
|
249
|
+
├── snpedia.sqlite ~390 MB
|
|
250
|
+
├── gwas.sqlite ~190 MB
|
|
251
|
+
└── pharmgkb.sqlite ~14 MB
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**The caches are disposable and re-downloadable.** Everything here was fetched by `allelix db update` and can be re-fetched at any time. There's no per-user state in the cache — your genotype files never enter this directory. Safe to delete to reclaim space (or to force a full refresh if you suspect a cache went stale):
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Reclaim all ~16-22 GB
|
|
258
|
+
rm -rf ~/.local/share/allelix/
|
|
259
|
+
|
|
260
|
+
# Reclaim everything except CADD (avoids re-downloading the slowest one)
|
|
261
|
+
find ~/.local/share/allelix/ -mindepth 1 ! -name 'cadd*.sqlite*' -delete
|
|
262
|
+
|
|
263
|
+
# Re-populate from scratch next time you analyze a file
|
|
264
|
+
allelix db update
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**The one thing worth backing up: `config.toml`.** It lives separately (XDG config dir, typically `~/.config/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings. It's the only non-reproducible state in the install. The caches will rebuild on the next `db update`; `config.toml` won't.
|
|
268
|
+
|
|
269
|
+
To fully uninstall allelix and reclaim everything:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
pip uninstall allelix
|
|
273
|
+
rm -rf ~/.local/share/allelix/ # reference database caches
|
|
274
|
+
rm -rf ~/.config/allelix/ # license + source-toggle config
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
A `allelix db clean` subcommand to automate the cache-clear step (with confirmation prompt and size report) is planned for v2.2 alongside `allelix db path` (prints the resolved data directory for scripting / backup integration).
|
|
278
|
+
|
|
217
279
|
## Data Sources & Licensing
|
|
218
280
|
|
|
219
281
|
Allelix source code is licensed under the **GNU Affero General Public License v3.0 or later** (AGPL-3.0-or-later). Allelix ships with **zero third-party data**. All reference databases are downloaded by the user at runtime via `allelix db update`. Each database retains its original license on the user's machine:
|
|
@@ -256,6 +318,42 @@ Of the 104,806 genotype pages in the archive:
|
|
|
256
318
|
|
|
257
319
|
None of these are scraping errors. They are editorial inconsistencies on the source wiki. The annotator handles all of them correctly: incomplete entries are skipped, variant title formats are matched, and no false annotations are produced.
|
|
258
320
|
|
|
321
|
+
## Troubleshooting
|
|
322
|
+
|
|
323
|
+
### `ImportError: cannot import name 'UTC' from 'datetime'`
|
|
324
|
+
|
|
325
|
+
You're on Python 3.10 or older. Allelix requires 3.11+. `python --version` to check; upgrade and reinstall.
|
|
326
|
+
|
|
327
|
+
### `allelix db update` failed partway through
|
|
328
|
+
|
|
329
|
+
Run it again. The update is idempotent — completed downloads are skipped (signal-matched against the upstream freshness probe), so re-running picks up where it left off. If a specific source keeps failing, you can isolate it: `allelix db update --no-gnomad` (or `--no-alphamissense` etc.) to skip the failing source for now, then retry that source standalone later.
|
|
330
|
+
|
|
331
|
+
If the failure is a network timeout on the same source repeatedly, the issue is likely on the source's end (HuggingFace / NCBI / EBI / etc. transient outage). Wait an hour and retry.
|
|
332
|
+
|
|
333
|
+
### `db status` shows an annotator as "not ready" but the cache files exist on disk
|
|
334
|
+
|
|
335
|
+
Almost always a schema or interpreter version bump in a recent allelix release that invalidates the pre-bump cache. The fix is `allelix db update` — it'll re-ingest only the affected annotators (everything else signal-skips). See the relevant `CHANGELOG.md` entry for "Cache invalidation" notes per release.
|
|
336
|
+
|
|
337
|
+
### Build mismatch warning when analyzing a known-good file
|
|
338
|
+
|
|
339
|
+
Either: (a) the file's header claims one build and the position data is actually a different build (real-world MHG / Tempus exports do this — header says 37, positions are 38; ADR-0021 documents the auto-detection), or (b) the file is a GRCh36-era export (pre-2012), which allelix detects accurately but doesn't ship a ClinVar cache for (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md) for the external-liftover path).
|
|
340
|
+
|
|
341
|
+
If you trust the file's header more than allelix's detection, force the build: `--build grch37` or `--build grch38`. The build banner in the analyze output will reflect what was actually used.
|
|
342
|
+
|
|
343
|
+
### Is my database stale? How fresh is the data?
|
|
344
|
+
|
|
345
|
+
`allelix db status` shows the ingestion date of each cache. The freshness model is signal-driven: `allelix db update` queries an upstream freshness signal (md5 / etag) per source and re-ingests only when the signal changed. There's no time-based staleness — a 6-month-old cache against an unchanged source IS current.
|
|
346
|
+
|
|
347
|
+
If you want to force a refresh: `allelix db update --force` re-downloads every cache regardless of freshness signal.
|
|
348
|
+
|
|
349
|
+
### Output reports contain real genome annotations
|
|
350
|
+
|
|
351
|
+
Anything `allelix analyze` writes to disk contains real annotations of your genome. Treat the output files (HTML, JSON, terminal redirects) as sensitive — they're as private as the input genotype file. Allelix does not transmit them; the lifecycle is local.
|
|
352
|
+
|
|
353
|
+
### Where does allelix store its data?
|
|
354
|
+
|
|
355
|
+
See [Managing your data](#managing-your-data) above for cache locations, the disposability rule, what to back up, and the uninstall procedure.
|
|
356
|
+
|
|
259
357
|
## Architecture & Design Decisions
|
|
260
358
|
|
|
261
359
|
The "why" behind major design choices lives in [`docs/adr/`](https://github.com/allelix/allelix/blob/main/docs/adr/README.md) as Architecture Decision Records. Read these before proposing changes that touch the parser/annotator interfaces, the regulatory posture, or the data-handling model.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "allelix"
|
|
7
|
-
version = "2.1.
|
|
7
|
+
version = "2.1.1"
|
|
8
8
|
description = "Open-source genotype analysis toolkit. Format-agnostic ingestion, database-agnostic annotation, offline-first."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -130,6 +130,52 @@ class TestAnnotation:
|
|
|
130
130
|
assert a.condition == ""
|
|
131
131
|
assert a.gene == ""
|
|
132
132
|
|
|
133
|
+
def test_annotation_to_public_dict_strips_internal_fields(self):
|
|
134
|
+
"""GH allelix-dev #3 — every internal-only field is excluded.
|
|
135
|
+
|
|
136
|
+
The contract: any field name in ``_INTERNAL_ANNOTATION_FIELDS`` MUST NOT
|
|
137
|
+
appear in the dict returned by ``annotation_to_public_dict``. If a new
|
|
138
|
+
internal field is added without registering it, this test fires.
|
|
139
|
+
"""
|
|
140
|
+
from allelix.models import (
|
|
141
|
+
_INTERNAL_ANNOTATION_FIELDS,
|
|
142
|
+
annotation_to_public_dict,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
a = self._minimal(is_must_include=True)
|
|
146
|
+
d = annotation_to_public_dict(a)
|
|
147
|
+
for field_name in _INTERNAL_ANNOTATION_FIELDS:
|
|
148
|
+
assert field_name not in d, (
|
|
149
|
+
f"{field_name!r} declared internal but leaked into public output dict"
|
|
150
|
+
)
|
|
151
|
+
# And spot-check a public field IS present (sanity for the inverse).
|
|
152
|
+
assert d["source"] == "clinvar"
|
|
153
|
+
assert d["rsid"] == "rs1"
|
|
154
|
+
|
|
155
|
+
def test_annotation_to_public_dict_used_by_all_three_serializers(self):
|
|
156
|
+
"""GH allelix-dev #3 — production serializers route through the
|
|
157
|
+
canonical helper, not inline filters.
|
|
158
|
+
|
|
159
|
+
If any future code path adds a 4th 'asdict(a) minus is_must_include'
|
|
160
|
+
inline filter, this test won't catch it directly — but the central
|
|
161
|
+
helper is the only place to remember the rule, and grep-ing for
|
|
162
|
+
``annotation_to_public_dict`` shows every consumer that respects it.
|
|
163
|
+
"""
|
|
164
|
+
from allelix.models import annotation_to_public_dict
|
|
165
|
+
from allelix.reports import json_report
|
|
166
|
+
from allelix.reports.diff import diff_annotation_to_dict
|
|
167
|
+
|
|
168
|
+
# All three production sites import the helper:
|
|
169
|
+
assert annotation_to_public_dict in vars(json_report).values()
|
|
170
|
+
# diff.py imports it at module scope too:
|
|
171
|
+
from allelix.reports import diff as diff_module
|
|
172
|
+
|
|
173
|
+
assert annotation_to_public_dict in vars(diff_module).values()
|
|
174
|
+
# Sanity: diff_annotation_to_dict exists and is callable
|
|
175
|
+
# (its body is exercised by tests/reports/test_diff.py — this is just
|
|
176
|
+
# the import-shape pin).
|
|
177
|
+
assert callable(diff_annotation_to_dict)
|
|
178
|
+
|
|
133
179
|
def test_default_references_independent_per_instance(self):
|
|
134
180
|
a1 = self._minimal()
|
|
135
181
|
a2 = self._minimal()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|