ont-bed-generator 0.4.0__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.
- ont_bed_generator-0.4.0/.github/workflows/ci.yml +38 -0
- ont_bed_generator-0.4.0/.github/workflows/release.yml +49 -0
- ont_bed_generator-0.4.0/.gitignore +13 -0
- ont_bed_generator-0.4.0/CHANGELOG.md +70 -0
- ont_bed_generator-0.4.0/CITATION.cff +16 -0
- ont_bed_generator-0.4.0/LICENSE +21 -0
- ont_bed_generator-0.4.0/PKG-INFO +143 -0
- ont_bed_generator-0.4.0/README.md +100 -0
- ont_bed_generator-0.4.0/annotation/README.md +96 -0
- ont_bed_generator-0.4.0/annotation/build_gene_gff.sh +31 -0
- ont_bed_generator-0.4.0/environment.yml +11 -0
- ont_bed_generator-0.4.0/pyproject.toml +63 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/__init__.py +25 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/__main__.py +7 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/_version.py +24 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/cli.py +76 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/intervals.py +70 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/io_inputs.py +128 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/io_outputs.py +30 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/model.py +56 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/ordering.py +20 -0
- ont_bed_generator-0.4.0/src/ont_bed_generator/resolve.py +57 -0
- ont_bed_generator-0.4.0/tests/conftest.py +23 -0
- ont_bed_generator-0.4.0/tests/data/mini.gff3 +7 -0
- ont_bed_generator-0.4.0/tests/data/mini.len +3 -0
- ont_bed_generator-0.4.0/tests/data/mini_genelist.tsv +6 -0
- ont_bed_generator-0.4.0/tests/test_intervals.py +120 -0
- ont_bed_generator-0.4.0/tests/test_io.py +51 -0
- ont_bed_generator-0.4.0/tests/test_resolve.py +49 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0 # required for hatch-vcs (tags)
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-python@v6
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install bedtools (parity test)
|
|
26
|
+
run: sudo apt-get update && sudo apt-get install -y bedtools
|
|
27
|
+
|
|
28
|
+
- name: Install package
|
|
29
|
+
run: pip install -e '.[dev]'
|
|
30
|
+
|
|
31
|
+
- name: Lint
|
|
32
|
+
run: ruff check .
|
|
33
|
+
|
|
34
|
+
- name: Type-check
|
|
35
|
+
run: mypy
|
|
36
|
+
|
|
37
|
+
- name: Tests
|
|
38
|
+
run: pytest -ra
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"] # étape 1 : dry-run TestPyPI
|
|
6
|
+
release:
|
|
7
|
+
types: [published] # étape 2 : production PyPI
|
|
8
|
+
workflow_dispatch: # dry-run TestPyPI manuel (voir la note sur v0.3.0)
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
with:
|
|
16
|
+
fetch-depth: 0 # tags requis par hatch-vcs
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with: { python-version: "3.x" }
|
|
19
|
+
- run: python -m pip install build
|
|
20
|
+
- run: python -m build
|
|
21
|
+
- run: pipx run twine check dist/*
|
|
22
|
+
- uses: actions/upload-artifact@v4
|
|
23
|
+
with: { name: dist, path: dist/ }
|
|
24
|
+
|
|
25
|
+
testpypi:
|
|
26
|
+
needs: build
|
|
27
|
+
if: github.event_name != 'release' # tag push OU dispatch manuel
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
environment: testpypi
|
|
30
|
+
permissions:
|
|
31
|
+
id-token: write
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/download-artifact@v4
|
|
34
|
+
with: { name: dist, path: dist/ }
|
|
35
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
36
|
+
with:
|
|
37
|
+
repository-url: https://test.pypi.org/legacy/
|
|
38
|
+
|
|
39
|
+
pypi:
|
|
40
|
+
needs: build
|
|
41
|
+
if: github.event_name == 'release' # Release GitHub publiée
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
environment: pypi
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/download-artifact@v4
|
|
48
|
+
with: { name: dist, path: dist/ }
|
|
49
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
5
|
+
versioning: [SemVer](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
### Added
|
|
9
|
+
- Transparent gzip (`.gz`) support for all input files (genome, genelist, GFF,
|
|
10
|
+
Entrez map).
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **Breaking:** simplified the genelist format to three columns
|
|
14
|
+
`Gene | Left_extension_bp | Right_extension_bp` (dropped the Chromosome,
|
|
15
|
+
Extended_region and Comment columns). The extended-region flag is now derived
|
|
16
|
+
(a gene is extended iff Left or Right is non-zero); a header row is
|
|
17
|
+
auto-detected. Existing six-column lists must be converted.
|
|
18
|
+
- Output BEDs are now sorted in canonical karyotypic order (chr1..chrN, then
|
|
19
|
+
X, Y, then M/MT, contigs last), organism-agnostic. The genome file's line
|
|
20
|
+
order no longer affects the output; `read_genome` returns sizes only and the
|
|
21
|
+
internal `rank` ordering was removed in favour of `ordering.chrom_sort_key`.
|
|
22
|
+
|
|
23
|
+
## [0.1.0] - TBD
|
|
24
|
+
### Added
|
|
25
|
+
- Generation of `targets.bed` and `merged-extended.bed`.
|
|
26
|
+
- Symbol resolution restricted to official HGNC symbols via the Entrez key
|
|
27
|
+
(GFF `Name=`), handling pseudoautosomal regions and IG/TCR loci.
|
|
28
|
+
- Reporting of invalid / ambiguous symbols (`--strict`).
|
|
29
|
+
- Unit tests + optional `bedtools merge` parity test.
|
|
30
|
+
|
|
31
|
+
## [0.2.0] - 2026-07-12
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- Structural invariant tests: merged intervals never overlap within a
|
|
35
|
+
`(chromosome, strand)` group, every interval stays within `[0, chrom_size]`,
|
|
36
|
+
and per-strand merging is idempotent.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- **Breaking — genelist format** is now `Gene | Left_extension_bp |
|
|
40
|
+
Right_extension_bp` with an auto-detected header. The Chromosome,
|
|
41
|
+
Extended_region and Comment columns were dropped; the extended-region flag is
|
|
42
|
+
now derived (a gene is extended iff Left or Right is non-zero).
|
|
43
|
+
- Output BEDs are sorted in canonical karyotypic order, independent of the
|
|
44
|
+
genome file's line order; `read_genome` now returns chromosome sizes only.
|
|
45
|
+
- `GffIndex.by_geneid` renamed to `geneid_to_features`.
|
|
46
|
+
- (internal) Split-line variable unified to `fields` across the readers.
|
|
47
|
+
|
|
48
|
+
### Removed
|
|
49
|
+
- Dead internal state: `GffGene.strand` and the never-read `geneid_name` index.
|
|
50
|
+
|
|
51
|
+
## [0.3.0] - 2026-07-13
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
- **Annotation dataset build pipeline** (`annotation/`). A scripted, reproducible
|
|
55
|
+
chain (`build_gene_gff.sh`) that derives a gene-feature-only, `chr`-named GFF3
|
|
56
|
+
for T2T-CHM13v2.0 from the official NCBI RefSeq release (RS_2025_08): it keeps
|
|
57
|
+
only `gene` features, renames RefSeq accessions to `chr` names via the
|
|
58
|
+
assembly report, and restores the pseudoautosomal chrX copy of *P2RY8*
|
|
59
|
+
(absent from the NCBI build) from the curated `NM_178129.5` coordinates under
|
|
60
|
+
the shared `GeneID:286530`, so *P2RY8* resolves on both chrX and chrY like
|
|
61
|
+
*CRLF2*. The built asset is archived on Zenodo
|
|
62
|
+
(DOI [10.5281/zenodo.21341240](https://doi.org/10.5281/zenodo.21341240)); the
|
|
63
|
+
pipeline and its documentation live in `annotation/`. No change to the package
|
|
64
|
+
code or CLI.
|
|
65
|
+
|
|
66
|
+
## [0.4.0] - 2026-07-14
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
- **Automated PiPy publishing workflow** (`.github/workflows/release.yml`).
|
|
70
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it as below."
|
|
3
|
+
title: "ont-bed-generator"
|
|
4
|
+
abstract: "Generate BED files for Oxford Nanopore adaptive sampling from a gene list and a RefSeq GFF3."
|
|
5
|
+
type: software
|
|
6
|
+
authors:
|
|
7
|
+
- family-names: Antoniewski
|
|
8
|
+
given-names: Christophe
|
|
9
|
+
orcid: "https://orcid.org/0000-0001-7709-2116"
|
|
10
|
+
license: MIT
|
|
11
|
+
repository-code: "https://github.com/CHANGE-ME/ont-bed-generator"
|
|
12
|
+
keywords:
|
|
13
|
+
- nanopore
|
|
14
|
+
- adaptive-sampling
|
|
15
|
+
- bioinformatics
|
|
16
|
+
- genomics
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christophe Antoniewski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ont-bed-generator
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Generate BED files for Oxford Nanopore adaptive sampling from a gene list and a RefSeq GFF3.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Institut-Leucemie/ont-bed-generator
|
|
6
|
+
Project-URL: Issues, https://github.com/Institut-Leucemie/ont-bed-generator/issues
|
|
7
|
+
Author: Christophe Antoniewski
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 Christophe Antoniewski
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: adaptive-sampling,bed,bioinformatics,genomics,nanopore
|
|
31
|
+
Classifier: Intended Audience :: Science/Research
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Operating System :: OS Independent
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
36
|
+
Requires-Python: >=3.10
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
39
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
41
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
# ont-bed-generator
|
|
47
|
+
|
|
48
|
+
Generate **BED** files for **Oxford Nanopore adaptive sampling** from a gene
|
|
49
|
+
list and a RefSeq GFF3. Standalone reimplementation (Python standard library
|
|
50
|
+
only, **no runtime dependencies**) of the Galaxy workflow "Adaptative ONT BED
|
|
51
|
+
file generation" (which replaced BED-Craft).
|
|
52
|
+
|
|
53
|
+
Given a list of target genes and a GFF3 annotation, the tool produces:
|
|
54
|
+
|
|
55
|
+
- `targets.bed` — the raw locus of each gene (BED6);
|
|
56
|
+
- `merged-extended.bed` — two intervals per locus (`+` / `-` strands),
|
|
57
|
+
extended, clamped to chromosome bounds, then merged per strand.
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# from source
|
|
63
|
+
git clone https://github.com/CHANGE-ME/ont-bed-generator.git
|
|
64
|
+
cd ont-bed-generator
|
|
65
|
+
pip install .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Conda development environment (includes `bedtools`, used only by the parity
|
|
69
|
+
test):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
conda env create -f environment.yml # or: mamba/micromamba env create -f environment.yml
|
|
73
|
+
conda activate ont-bed-generator
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
ont-bed-generator \
|
|
80
|
+
--genelist genelist.tsv \
|
|
81
|
+
--gff annotation.genes.gff3 \
|
|
82
|
+
--genome hs1.len \
|
|
83
|
+
--out-targets targets.bed \
|
|
84
|
+
--out-merged merged-extended.bed
|
|
85
|
+
# or: python -m ont_bed_generator --genelist ... --gff ... --genome ...
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Inputs
|
|
89
|
+
|
|
90
|
+
1. **genelist TSV** — columns `Gene | Left_extension_bp | Right_extension_bp`,
|
|
91
|
+
with an (auto-detected) header row. Coordinates come from the GFF, so no
|
|
92
|
+
chromosome column is needed; a gene is treated as an extended region when
|
|
93
|
+
Left or Right is non-zero (no separate flag). A bare `Gene` line (no
|
|
94
|
+
extensions) is valid and gets only the default flank. Symbols must be
|
|
95
|
+
**official HGNC symbols** (check them with the
|
|
96
|
+
[multi-symbol checker](https://www.genenames.org/tools/multi-symbol-checker/)).
|
|
97
|
+
2. **GFF3** — a RefSeq GFF3 whose `gene` features carry `Name=` and
|
|
98
|
+
`Dbxref=GeneID:`. The **full NCBI annotation works as-is**: the tool reads
|
|
99
|
+
only `gene` features and ignores everything else, so pre-extracting them is
|
|
100
|
+
just an optional speed-up. Gzip-compressed (`.gz`) input is read transparently
|
|
101
|
+
(this applies to every input file). Example (T2T-CHM13v2.0 / hs1):
|
|
102
|
+
```bash
|
|
103
|
+
curl -sO https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/009/914/755/GCF_009914755.1_T2T-CHM13v2.0/GCF_009914755.1_T2T-CHM13v2.0_genomic.gff.gz
|
|
104
|
+
```
|
|
105
|
+
3. **genome sizes** — `chrom<TAB>size`, one line per chromosome. Used for
|
|
106
|
+
telomere clamping only; the line order does not matter (output BEDs are
|
|
107
|
+
sorted in canonical karyotypic order).
|
|
108
|
+
```bash
|
|
109
|
+
curl -sO https://hgdownload.soe.ucsc.edu/goldenPath/hs1/bigZips/hs1.chrom.sizes
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Symbol resolution
|
|
113
|
+
|
|
114
|
+
Official symbol → GeneID (GFF `Name=`, or external `--entrez-map`) → collect
|
|
115
|
+
**all** loci carrying that GeneID. The Entrez key handles pseudoautosomal
|
|
116
|
+
regions (one GeneID on chrX and chrY yields two loci) and IG/TCR loci. A symbol
|
|
117
|
+
that is not found, or that maps to several GeneIDs, is **reported** (on stderr),
|
|
118
|
+
never guessed. `--strict` returns a non-zero exit code if any symbol is invalid
|
|
119
|
+
or ambiguous.
|
|
120
|
+
|
|
121
|
+
## Development
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install -e '.[dev]'
|
|
125
|
+
pytest # unit tests (+ bedtools parity if bedtools is present)
|
|
126
|
+
ruff check .
|
|
127
|
+
mypy
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Versioning is driven by git tags (`hatch-vcs`): a release is
|
|
131
|
+
`git tag vX.Y.Z && git push --tags`.
|
|
132
|
+
|
|
133
|
+
## Intentional differences from the original Galaxy workflow
|
|
134
|
+
|
|
135
|
+
- The workflow patched non-official symbols (`MKL1→MKLN1`, a bug — MKLN1 is a
|
|
136
|
+
different gene; `LYL→LYL1`). Here **no patching**: such symbols are reported
|
|
137
|
+
for correction at the source.
|
|
138
|
+
- The Galaxy output contained a missing merge (`bedtools merge` on
|
|
139
|
+
genome-order-sorted input without `-g`). This tool performs the correct merge.
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# ont-bed-generator
|
|
4
|
+
|
|
5
|
+
Generate **BED** files for **Oxford Nanopore adaptive sampling** from a gene
|
|
6
|
+
list and a RefSeq GFF3. Standalone reimplementation (Python standard library
|
|
7
|
+
only, **no runtime dependencies**) of the Galaxy workflow "Adaptative ONT BED
|
|
8
|
+
file generation" (which replaced BED-Craft).
|
|
9
|
+
|
|
10
|
+
Given a list of target genes and a GFF3 annotation, the tool produces:
|
|
11
|
+
|
|
12
|
+
- `targets.bed` — the raw locus of each gene (BED6);
|
|
13
|
+
- `merged-extended.bed` — two intervals per locus (`+` / `-` strands),
|
|
14
|
+
extended, clamped to chromosome bounds, then merged per strand.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# from source
|
|
20
|
+
git clone https://github.com/CHANGE-ME/ont-bed-generator.git
|
|
21
|
+
cd ont-bed-generator
|
|
22
|
+
pip install .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Conda development environment (includes `bedtools`, used only by the parity
|
|
26
|
+
test):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
conda env create -f environment.yml # or: mamba/micromamba env create -f environment.yml
|
|
30
|
+
conda activate ont-bed-generator
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ont-bed-generator \
|
|
37
|
+
--genelist genelist.tsv \
|
|
38
|
+
--gff annotation.genes.gff3 \
|
|
39
|
+
--genome hs1.len \
|
|
40
|
+
--out-targets targets.bed \
|
|
41
|
+
--out-merged merged-extended.bed
|
|
42
|
+
# or: python -m ont_bed_generator --genelist ... --gff ... --genome ...
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Inputs
|
|
46
|
+
|
|
47
|
+
1. **genelist TSV** — columns `Gene | Left_extension_bp | Right_extension_bp`,
|
|
48
|
+
with an (auto-detected) header row. Coordinates come from the GFF, so no
|
|
49
|
+
chromosome column is needed; a gene is treated as an extended region when
|
|
50
|
+
Left or Right is non-zero (no separate flag). A bare `Gene` line (no
|
|
51
|
+
extensions) is valid and gets only the default flank. Symbols must be
|
|
52
|
+
**official HGNC symbols** (check them with the
|
|
53
|
+
[multi-symbol checker](https://www.genenames.org/tools/multi-symbol-checker/)).
|
|
54
|
+
2. **GFF3** — a RefSeq GFF3 whose `gene` features carry `Name=` and
|
|
55
|
+
`Dbxref=GeneID:`. The **full NCBI annotation works as-is**: the tool reads
|
|
56
|
+
only `gene` features and ignores everything else, so pre-extracting them is
|
|
57
|
+
just an optional speed-up. Gzip-compressed (`.gz`) input is read transparently
|
|
58
|
+
(this applies to every input file). Example (T2T-CHM13v2.0 / hs1):
|
|
59
|
+
```bash
|
|
60
|
+
curl -sO https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/009/914/755/GCF_009914755.1_T2T-CHM13v2.0/GCF_009914755.1_T2T-CHM13v2.0_genomic.gff.gz
|
|
61
|
+
```
|
|
62
|
+
3. **genome sizes** — `chrom<TAB>size`, one line per chromosome. Used for
|
|
63
|
+
telomere clamping only; the line order does not matter (output BEDs are
|
|
64
|
+
sorted in canonical karyotypic order).
|
|
65
|
+
```bash
|
|
66
|
+
curl -sO https://hgdownload.soe.ucsc.edu/goldenPath/hs1/bigZips/hs1.chrom.sizes
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Symbol resolution
|
|
70
|
+
|
|
71
|
+
Official symbol → GeneID (GFF `Name=`, or external `--entrez-map`) → collect
|
|
72
|
+
**all** loci carrying that GeneID. The Entrez key handles pseudoautosomal
|
|
73
|
+
regions (one GeneID on chrX and chrY yields two loci) and IG/TCR loci. A symbol
|
|
74
|
+
that is not found, or that maps to several GeneIDs, is **reported** (on stderr),
|
|
75
|
+
never guessed. `--strict` returns a non-zero exit code if any symbol is invalid
|
|
76
|
+
or ambiguous.
|
|
77
|
+
|
|
78
|
+
## Development
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install -e '.[dev]'
|
|
82
|
+
pytest # unit tests (+ bedtools parity if bedtools is present)
|
|
83
|
+
ruff check .
|
|
84
|
+
mypy
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Versioning is driven by git tags (`hatch-vcs`): a release is
|
|
88
|
+
`git tag vX.Y.Z && git push --tags`.
|
|
89
|
+
|
|
90
|
+
## Intentional differences from the original Galaxy workflow
|
|
91
|
+
|
|
92
|
+
- The workflow patched non-official symbols (`MKL1→MKLN1`, a bug — MKLN1 is a
|
|
93
|
+
different gene; `LYL→LYL1`). Here **no patching**: such symbols are reported
|
|
94
|
+
for correction at the source.
|
|
95
|
+
- The Galaxy output contained a missing merge (`bedtools merge` on
|
|
96
|
+
genome-order-sorted input without `-g`). This tool performs the correct merge.
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# T2T-CHM13v2.0 gene annotation asset for ont-bed-generator
|
|
2
|
+
|
|
3
|
+
`T2T-CHM13v2.0_genes.chr.gff3.gz` — a **gene-feature-only**, **`chr`-named** GFF3
|
|
4
|
+
derived from the official NCBI RefSeq annotation of T2T-CHM13v2.0, with one
|
|
5
|
+
documented addition (the chrX pseudoautosomal copy of *P2RY8*).
|
|
6
|
+
|
|
7
|
+
It is the annotation input for `ont-bed-generator --gff`. Everything below is
|
|
8
|
+
reproducible with `build_gene_gff.sh`; nothing is hand-edited except the single,
|
|
9
|
+
sourced line described in step 3.
|
|
10
|
+
|
|
11
|
+
> **The pre-built asset is archived on Zenodo:**
|
|
12
|
+
> [](https://doi.org/10.5281/zenodo.21341240)
|
|
13
|
+
> Download it there, or rebuild it byte-for-byte with `build_gene_gff.sh`.
|
|
14
|
+
|
|
15
|
+
## Source
|
|
16
|
+
|
|
17
|
+
| | |
|
|
18
|
+
|---|---|
|
|
19
|
+
| Assembly | T2T-CHM13v2.0 (`GCF_009914755.1`) |
|
|
20
|
+
| Annotation release | NCBI RefSeq `RS_2025_08` |
|
|
21
|
+
| GFF | `.../GCF_009914755.1_T2T-CHM13v2.0_genomic.gff.gz` |
|
|
22
|
+
| Assembly report | `.../GCF_009914755.1_T2T-CHM13v2.0_assembly_report.txt` |
|
|
23
|
+
|
|
24
|
+
Base URL: `https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/009/914/755/GCF_009914755.1_T2T-CHM13v2.0`
|
|
25
|
+
|
|
26
|
+
## Production chain (`build_gene_gff.sh`)
|
|
27
|
+
|
|
28
|
+
1. **Contig map.** Build a `RefSeq-accession -> chr` table from the official
|
|
29
|
+
`assembly_report.txt` (column *RefSeq-Accn* -> column *UCSC-style-name*). No
|
|
30
|
+
hard-coded list: the mapping is read from NCBI's own file.
|
|
31
|
+
2. **Filter + rename.** Keep only `gene` features (column 3 == `gene`; the only
|
|
32
|
+
thing ont-bed-generator reads) and rename column 1 from the RefSeq accession
|
|
33
|
+
to the `chr` name using the map. Comment/header lines are dropped. This turns
|
|
34
|
+
the ~78 MB full annotation into a ~2 MB gene-only file (41 561 genes).
|
|
35
|
+
3. **PAR patch — one added line, fully sourced.** The NCBI `genomic.gff` build
|
|
36
|
+
annotates *P2RY8* only on chrY, although *P2RY8* is a **PAR1 gene** and is
|
|
37
|
+
therefore present on both sex chromosomes (its neighbour *CRLF2*, also PAR1,
|
|
38
|
+
*is* annotated on both by NCBI). We add the missing chrX copy:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
chrX NCBI_RefSeq_curated_NM_178129.5_PAR1 gene 1304499 1338509 . - . \
|
|
42
|
+
ID=gene-P2RY8-chrX-PAR1;Dbxref=GeneID:286530;Name=P2RY8;gene_biotype=protein_coding
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- **Coordinates are not invented.** They are the alignment of NCBI's curated
|
|
46
|
+
transcript `NM_178129.5` to chrX of hs1, as distributed by UCSC
|
|
47
|
+
(`chrX:1304499-1338509`). Confirmed against HGNC (symbol `P2RY8` <-> `ENSG`
|
|
48
|
+
<-> Entrez `286530`) and against the PAR1 gene lists.
|
|
49
|
+
- **Same `GeneID:286530` as the chrY copy**, so ont-bed-generator groups both
|
|
50
|
+
loci under one gene and resolves *P2RY8* to **both** chrX and chrY (it is
|
|
51
|
+
*not* flagged ambiguous — same mechanism as *CRLF2*).
|
|
52
|
+
- The `source` column records the provenance so the line is auditable in-file.
|
|
53
|
+
|
|
54
|
+
4. **Compress.** `gzip` the result. Feature order is irrelevant —
|
|
55
|
+
ont-bed-generator sorts karyotypically itself.
|
|
56
|
+
|
|
57
|
+
## Get the asset
|
|
58
|
+
|
|
59
|
+
Either download the archived build from Zenodo
|
|
60
|
+
([10.5281/zenodo.21341240](https://doi.org/10.5281/zenodo.21341240)), or
|
|
61
|
+
regenerate it:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
bash build_gene_gff.sh # -> T2T-CHM13v2.0_genes.chr.gff3.gz
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Both routes yield the same file for a given NCBI release.
|
|
68
|
+
|
|
69
|
+
## Verification (performed on this asset)
|
|
70
|
+
|
|
71
|
+
- 41 562 lines, **all** `gene` features (0 non-gene).
|
|
72
|
+
- `P2RY8` present on chrX (1304499-1338509) **and** chrY (1325951-1401516).
|
|
73
|
+
- `ont-bed-generator` resolves both `P2RY8` and `CRLF2` to two loci each, with
|
|
74
|
+
**no** invalid and **no** ambiguous symbols.
|
|
75
|
+
|
|
76
|
+
## Scope & limitations
|
|
77
|
+
|
|
78
|
+
- Only *P2RY8* is patched. *CRLF2* needs no patch (NCBI annotates it on both X
|
|
79
|
+
and Y natively). No other PAR1 gene in the target panels required a chrX/chrY
|
|
80
|
+
copy; audit the PAR1 gene list against this file if your panel adds one.
|
|
81
|
+
- PAR-gene annotation is **inconsistent across all sources** (NCBI, UCSC,
|
|
82
|
+
Ensembl each differ and each is incomplete for PAR1). This asset takes the
|
|
83
|
+
NCBI annotation as the traceable base and adds the single, biologically
|
|
84
|
+
required, NCBI-sourced chrX *P2RY8* copy — rather than switching to a source
|
|
85
|
+
with its own, different PAR gaps.
|
|
86
|
+
- Tied to NCBI release `RS_2025_08`. Regenerate (and deposit a new Zenodo
|
|
87
|
+
version) when NCBI publishes a new annotation release — ideally after the
|
|
88
|
+
upstream *P2RY8* chrX omission is fixed, at which point step 3 becomes
|
|
89
|
+
unnecessary.
|
|
90
|
+
|
|
91
|
+
## Citation
|
|
92
|
+
|
|
93
|
+
If you use this asset, please cite the Zenodo record:
|
|
94
|
+
**DOI [10.5281/zenodo.21341240](https://doi.org/10.5281/zenodo.21341240)**.
|
|
95
|
+
It derives from the NCBI RefSeq annotation of T2T-CHM13v2.0 (`GCF_009914755.1`,
|
|
96
|
+
release RS_2025_08); please also credit NCBI RefSeq as the primary source.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Produce the gene-only, chr-named, PAR-patched GFF3 asset consumed by
|
|
3
|
+
# ont-bed-generator, from the official NCBI RefSeq T2T-CHM13v2.0 annotation.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
BASE="https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/009/914/755/GCF_009914755.1_T2T-CHM13v2.0"
|
|
7
|
+
GFF_URL="$BASE/GCF_009914755.1_T2T-CHM13v2.0_genomic.gff.gz"
|
|
8
|
+
REPORT_URL="$BASE/GCF_009914755.1_T2T-CHM13v2.0_assembly_report.txt"
|
|
9
|
+
OUT="T2T-CHM13v2.0_genes.chr.gff3.gz"
|
|
10
|
+
|
|
11
|
+
# 1) accession -> chr map, from the official assembly report
|
|
12
|
+
curl -s "$REPORT_URL" | tr -d '\r' \
|
|
13
|
+
| awk -F'\t' '!/^#/ && $7!="na"{print $7"\t"$10}' > map.tsv
|
|
14
|
+
|
|
15
|
+
# 2) keep only 'gene' features + rename column 1 (accession -> chr); drop comments
|
|
16
|
+
curl -s "$GFF_URL" | zcat \
|
|
17
|
+
| awk -F'\t' -v OFS='\t' '
|
|
18
|
+
NR==FNR { m[$1]=$2; next }
|
|
19
|
+
/^#/ { next }
|
|
20
|
+
$3=="gene" { if ($1 in m) $1=m[$1]; print }
|
|
21
|
+
' map.tsv - > genes.chr.gff3
|
|
22
|
+
|
|
23
|
+
# 3) append the chrX PAR1 copy of P2RY8 (absent from the NCBI annotation build).
|
|
24
|
+
# Coordinates = NCBI curated transcript NM_178129.5 aligned to chrX (hs1),
|
|
25
|
+
# same GeneID:286530 as the chrY copy -> ont-bed resolves both loci, not ambiguous.
|
|
26
|
+
STRAND=$(awk -F'\t' 'index($9,"Name=P2RY8;"){print $7; exit}' genes.chr.gff3)
|
|
27
|
+
printf 'chrX\tNCBI_RefSeq_curated_NM_178129.5_PAR1\tgene\t1304499\t1338509\t.\t%s\t.\tID=gene-P2RY8-chrX-PAR1;Dbxref=GeneID:286530;Name=P2RY8;gene_biotype=protein_coding\n' "$STRAND" >> genes.chr.gff3
|
|
28
|
+
|
|
29
|
+
# 4) compress (order is irrelevant; ont-bed-generator sorts karyotypically itself)
|
|
30
|
+
gzip -c genes.chr.gff3 > "$OUT"
|
|
31
|
+
echo "written: $OUT"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ont-bed-generator"
|
|
7
|
+
description = "Generate BED files for Oxford Nanopore adaptive sampling from a gene list and a RefSeq GFF3."
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
license = { file = "LICENSE" }
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [{ name = "Christophe Antoniewski" }]
|
|
12
|
+
keywords = ["nanopore", "adaptive-sampling", "bed", "bioinformatics", "genomics"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
20
|
+
# No runtime dependencies: standard library only.
|
|
21
|
+
dependencies = []
|
|
22
|
+
dynamic = ["version"]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/Institut-Leucemie/ont-bed-generator"
|
|
26
|
+
Issues = "https://github.com/Institut-Leucemie/ont-bed-generator/issues"
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = ["pytest>=7", "ruff>=0.4", "mypy>=1.8", "build>=1.2"]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
ont-bed-generator = "ont_bed_generator.cli:main"
|
|
33
|
+
|
|
34
|
+
# ── Versioning driven by git tags (hatch-vcs) ──
|
|
35
|
+
[tool.hatch.version]
|
|
36
|
+
source = "vcs"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.hooks.vcs]
|
|
39
|
+
version-file = "src/ont_bed_generator/_version.py"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["src/ont_bed_generator"]
|
|
43
|
+
|
|
44
|
+
# ── Quality tools ──
|
|
45
|
+
[tool.ruff]
|
|
46
|
+
line-length = 100
|
|
47
|
+
target-version = "py310"
|
|
48
|
+
|
|
49
|
+
[tool.ruff.lint]
|
|
50
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
51
|
+
|
|
52
|
+
[tool.mypy]
|
|
53
|
+
python_version = "3.10"
|
|
54
|
+
strict = true
|
|
55
|
+
files = ["src"]
|
|
56
|
+
|
|
57
|
+
[[tool.mypy.overrides]]
|
|
58
|
+
module = "ont_bed_generator._version"
|
|
59
|
+
ignore_missing_imports = true
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
addopts = "-ra"
|