scanitd 0.9.2__tar.gz → 0.9.3__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.
- scanitd-0.9.3/.github/workflows/release.yml +88 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/.gitignore +4 -1
- {scanitd-0.9.2 → scanitd-0.9.3}/PKG-INFO +29 -10
- {scanitd-0.9.2 → scanitd-0.9.3}/README.md +27 -7
- scanitd-0.9.3/conda/meta.yaml +53 -0
- scanitd-0.9.3/docs/source/changelog.md +81 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/conf.py +1 -1
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/guide/usage.md +2 -1
- {scanitd-0.9.2 → scanitd-0.9.3}/pyproject.toml +13 -2
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/__init__.py +1 -1
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/cli/cli.py +16 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/inference/helper.py +10 -5
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/inference/main.py +12 -1
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/inference/sr_resuer.py +5 -4
- scanitd-0.9.3/src/scanitd/ssw/LICENSE-ssw-py.txt +58 -0
- scanitd-0.9.3/src/scanitd/ssw/Makefile +17 -0
- scanitd-0.9.3/src/scanitd/ssw/__init__.py +9 -0
- scanitd-0.9.3/src/scanitd/ssw/alignment.py +175 -0
- scanitd-0.9.3/src/scanitd/ssw/build_ssw.py +45 -0
- scanitd-0.9.3/src/scanitd/ssw/sse2neon.h +9074 -0
- scanitd-0.9.3/src/scanitd/ssw/ssw.c +995 -0
- scanitd-0.9.3/src/scanitd/ssw/ssw.h +194 -0
- scanitd-0.9.3/src/scanitd/ssw/str_util.c +138 -0
- scanitd-0.9.3/src/scanitd/ssw/str_util.h +51 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/tests/test_helper.py +20 -4
- scanitd-0.9.3/tests/test_main.py +112 -0
- scanitd-0.9.3/tests/test_sr_resuer.py +135 -0
- scanitd-0.9.3/tests/test_ssw.py +117 -0
- scanitd-0.9.2/.github/workflows/release.yml +0 -50
- scanitd-0.9.2/docs/source/changelog.md +0 -43
- {scanitd-0.9.2 → scanitd-0.9.3}/.pre-commit-config.yaml +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/.readthedocs.yaml +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/CODE_OF_CONDUCT.md +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/CONTRIBUTING.md +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/LICENSE +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/MANIFEST.in +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/Makefile +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/Makefile +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/make.bat +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/api/base.rst +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/api/cli.rst +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/api/inference.rst +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/api/writer.rst +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/contributing.md +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/guide/installation.md +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/guide/output.md +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/guide/quickstart.md +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/docs/source/index.rst +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/requirements_dev.txt +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/base/__init__.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/base/basic.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/base/basic_read.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/base/cigar.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/cli/__init__.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/inference/__init__.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/mtype.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/py.typed +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/writer/__init__.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/writer/vcf_writer.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/src/scanitd/writer/writer.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/tests/__init__.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/tests/conftest.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/tests/test_basic.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/tests/test_basic_read.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/tests/test_cigar.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/tests/test_scanitd.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/tests/test_vcf_writer.py +0 -0
- {scanitd-0.9.2 → scanitd-0.9.3}/tox.ini +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
name: Release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Run test suite
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Install uv
|
|
16
|
+
uses: astral-sh/setup-uv@v3
|
|
17
|
+
|
|
18
|
+
- name: Install dependencies
|
|
19
|
+
run: uv sync --group dev
|
|
20
|
+
|
|
21
|
+
- name: Run tests
|
|
22
|
+
run: uv run pytest
|
|
23
|
+
|
|
24
|
+
build_sdist:
|
|
25
|
+
name: Build source distribution
|
|
26
|
+
needs: test
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- name: Install uv
|
|
32
|
+
uses: astral-sh/setup-uv@v3
|
|
33
|
+
|
|
34
|
+
- name: Build sdist
|
|
35
|
+
run: uv build --sdist
|
|
36
|
+
|
|
37
|
+
- name: Upload sdist artifact
|
|
38
|
+
uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: pypi-sdist
|
|
41
|
+
path: dist/
|
|
42
|
+
|
|
43
|
+
build_wheels:
|
|
44
|
+
name: Build wheels (${{ matrix.os }})
|
|
45
|
+
needs: test
|
|
46
|
+
runs-on: ${{ matrix.os }}
|
|
47
|
+
strategy:
|
|
48
|
+
matrix:
|
|
49
|
+
os: [ubuntu-latest, macos-latest]
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
|
|
53
|
+
- name: Build wheels with cibuildwheel
|
|
54
|
+
uses: pypa/cibuildwheel@v4.3.5
|
|
55
|
+
with:
|
|
56
|
+
output-dir: dist
|
|
57
|
+
|
|
58
|
+
- name: Upload wheel artifacts
|
|
59
|
+
uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: pypi-wheels-${{ matrix.os }}
|
|
62
|
+
path: dist/
|
|
63
|
+
|
|
64
|
+
publish:
|
|
65
|
+
name: Publish to PyPI
|
|
66
|
+
needs: [build_sdist, build_wheels]
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
environment:
|
|
69
|
+
name: pypi
|
|
70
|
+
url: https://pypi.org/p/scanitd
|
|
71
|
+
permissions:
|
|
72
|
+
id-token: write
|
|
73
|
+
steps:
|
|
74
|
+
- name: Download sdist artifact
|
|
75
|
+
uses: actions/download-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: pypi-sdist
|
|
78
|
+
path: dist/
|
|
79
|
+
|
|
80
|
+
- name: Download wheel artifacts
|
|
81
|
+
uses: actions/download-artifact@v4
|
|
82
|
+
with:
|
|
83
|
+
pattern: pypi-wheels-*
|
|
84
|
+
path: dist/
|
|
85
|
+
merge-multiple: true
|
|
86
|
+
|
|
87
|
+
- name: Publish to PyPI
|
|
88
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -3,8 +3,9 @@ __pycache__/
|
|
|
3
3
|
*.py[cod]
|
|
4
4
|
*$py.class
|
|
5
5
|
|
|
6
|
-
# C extensions
|
|
6
|
+
# C extensions and object files
|
|
7
7
|
*.so
|
|
8
|
+
*.o
|
|
8
9
|
|
|
9
10
|
# Distribution / packaging
|
|
10
11
|
.Python
|
|
@@ -116,4 +117,6 @@ ENV/
|
|
|
116
117
|
# Tool configurations
|
|
117
118
|
.omc/
|
|
118
119
|
.serena/
|
|
120
|
+
BIOCONDA.md
|
|
121
|
+
CLAUDE.md
|
|
119
122
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: scanitd
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: ScanITD
|
|
5
5
|
Project-URL: Homepage, https://github.com/ylab-hi/ScanITD
|
|
6
6
|
Project-URL: Repository, https://github.com/ylab-hi/ScanITD
|
|
@@ -22,7 +22,6 @@ Requires-Dist: psutil>=5.9.5
|
|
|
22
22
|
Requires-Dist: pyfaidx>=0.7.2
|
|
23
23
|
Requires-Dist: pysam>=0.22.0
|
|
24
24
|
Requires-Dist: rich>=13.7.0
|
|
25
|
-
Requires-Dist: ssw-py>=1.0.1
|
|
26
25
|
Requires-Dist: typer>=0.12.5
|
|
27
26
|
Provides-Extra: docs
|
|
28
27
|
Requires-Dist: myst-parser==3.0.0; extra == 'docs'
|
|
@@ -35,15 +34,33 @@ Description-Content-Type: text/markdown
|
|
|
35
34
|
# ScanITD
|
|
36
35
|
|
|
37
36
|
[](https://pypi.python.org/pypi/scanitd)
|
|
38
|
-
[](https://anaconda.org/bioconda/scanitd)
|
|
38
|
+
[](https://pypi.python.org/pypi/scanitd/#files)
|
|
39
39
|
[](https://github.com/ylab-hi/ScanITD/blob/main/LICENSE)
|
|
40
40
|
[](https://scanitd.readthedocs.io)
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
# 📦 Installation
|
|
44
44
|
|
|
45
|
-
ScanITD can be installed using
|
|
46
|
-
|
|
45
|
+
ScanITD can be installed using **Conda** (via the Bioconda channel) or **pip**.
|
|
46
|
+
|
|
47
|
+
### Via Conda (Bioconda)
|
|
48
|
+
|
|
49
|
+
To install ScanITD using Conda:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
conda install -c bioconda scanitd
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or using the modern `bioconda::scanitd` specifier:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
conda install bioconda::scanitd
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Via Pip
|
|
62
|
+
|
|
63
|
+
ScanITD can also be installed using pip, the Python package installer:
|
|
47
64
|
|
|
48
65
|
1. Ensure you have Python 3.10 or later installed on your system.
|
|
49
66
|
|
|
@@ -60,11 +77,13 @@ Follow these steps to install:
|
|
|
60
77
|
pip install scanitd
|
|
61
78
|
```
|
|
62
79
|
|
|
63
|
-
|
|
80
|
+
### Verification
|
|
64
81
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
Verify the installation:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
scanitd --help
|
|
86
|
+
```
|
|
68
87
|
|
|
69
88
|
# 🛠️ Usage
|
|
70
89
|
|
|
@@ -1,15 +1,33 @@
|
|
|
1
1
|
# ScanITD
|
|
2
2
|
|
|
3
3
|
[](https://pypi.python.org/pypi/scanitd)
|
|
4
|
-
[](https://anaconda.org/bioconda/scanitd)
|
|
5
|
+
[](https://pypi.python.org/pypi/scanitd/#files)
|
|
5
6
|
[](https://github.com/ylab-hi/ScanITD/blob/main/LICENSE)
|
|
6
7
|
[](https://scanitd.readthedocs.io)
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
# 📦 Installation
|
|
10
11
|
|
|
11
|
-
ScanITD can be installed using
|
|
12
|
-
|
|
12
|
+
ScanITD can be installed using **Conda** (via the Bioconda channel) or **pip**.
|
|
13
|
+
|
|
14
|
+
### Via Conda (Bioconda)
|
|
15
|
+
|
|
16
|
+
To install ScanITD using Conda:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
conda install -c bioconda scanitd
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or using the modern `bioconda::scanitd` specifier:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
conda install bioconda::scanitd
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Via Pip
|
|
29
|
+
|
|
30
|
+
ScanITD can also be installed using pip, the Python package installer:
|
|
13
31
|
|
|
14
32
|
1. Ensure you have Python 3.10 or later installed on your system.
|
|
15
33
|
|
|
@@ -26,11 +44,13 @@ Follow these steps to install:
|
|
|
26
44
|
pip install scanitd
|
|
27
45
|
```
|
|
28
46
|
|
|
29
|
-
|
|
47
|
+
### Verification
|
|
30
48
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
49
|
+
Verify the installation:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
scanitd --help
|
|
53
|
+
```
|
|
34
54
|
|
|
35
55
|
# 🛠️ Usage
|
|
36
56
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{% set name = "scanitd" %}
|
|
2
|
+
{% set version = "0.9.3" %}
|
|
3
|
+
|
|
4
|
+
package:
|
|
5
|
+
name: {{ name|lower }}
|
|
6
|
+
version: {{ version }}
|
|
7
|
+
|
|
8
|
+
source:
|
|
9
|
+
url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/scanitd-{{ version }}.tar.gz
|
|
10
|
+
sha256: 554efdf1c206a332fca1933a2ffb765d26127e22aaacadf86d170d21cbe288a6 # TODO: update sha256 after PyPI upload of 0.9.3
|
|
11
|
+
|
|
12
|
+
build:
|
|
13
|
+
number: 0
|
|
14
|
+
run_exports:
|
|
15
|
+
- {{ pin_subpackage('scanitd', max_pin="x.x") }}
|
|
16
|
+
script: {{ PYTHON }} -m pip install . -vv --no-deps --no-build-isolation
|
|
17
|
+
entry_points:
|
|
18
|
+
- scanitd = scanitd.cli.cli:app
|
|
19
|
+
|
|
20
|
+
requirements:
|
|
21
|
+
build:
|
|
22
|
+
- {{ compiler('c') }}
|
|
23
|
+
- make
|
|
24
|
+
host:
|
|
25
|
+
- python >=3.9
|
|
26
|
+
- pip
|
|
27
|
+
- hatchling
|
|
28
|
+
run:
|
|
29
|
+
- python >=3.9
|
|
30
|
+
- numpy >=2.0.0
|
|
31
|
+
- pysam >=0.22.0
|
|
32
|
+
- pyfaidx >=0.7.2
|
|
33
|
+
- psutil >=5.9.5
|
|
34
|
+
- loguru >=0.7.0
|
|
35
|
+
- typer >=0.12.5
|
|
36
|
+
- rich >=13.7.0
|
|
37
|
+
|
|
38
|
+
test:
|
|
39
|
+
commands:
|
|
40
|
+
- scanitd --help
|
|
41
|
+
|
|
42
|
+
about:
|
|
43
|
+
home: https://github.com/ylab-hi/ScanITD
|
|
44
|
+
summary: Detecting internal tandem duplication with robust variant allele frequency estimation
|
|
45
|
+
license: MIT
|
|
46
|
+
license_file: LICENSE
|
|
47
|
+
doc_url: https://scanitd.readthedocs.io
|
|
48
|
+
|
|
49
|
+
extra:
|
|
50
|
+
identifiers:
|
|
51
|
+
- doi:10.1093/gigascience/giaa089
|
|
52
|
+
recipe-maintainers:
|
|
53
|
+
- dolittle007
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to ScanITD will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [0.9.3] — 2026-11-13
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Removed the 8,000x `pysam.pileup()` depth cap that caused AF underestimation on deep amplicon data (now uncapped by default via `2**31 - 1`, configurable with `--max-depth`)
|
|
14
|
+
- Converted 0-based BED start coordinates (`start + 1`) to 1-based samtools region strings in `parse_target_genomic_coordinates()`, preventing `ValueError: start out of range (-1)` when a BED interval starts at `0`
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Vendored the MIT-licensed Striped Smith-Waterman (SSW) C library and `ctypes` Python wrapper under `src/scanitd/ssw`, removing the external `ssw-py` dependency
|
|
18
|
+
- Added a Hatchling custom build hook (`src/scanitd/ssw/build_ssw.py`) to compile `ssw.so` from source during wheel builds (with `sse2neon.h` support for ARM64)
|
|
19
|
+
- Updated `conda/meta.yaml` for native C extension builds (`{{ compiler('c') }}`, `make`) and removed `ssw-py`
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- Unit tests for `scanitd.ssw.AlignmentMgr` and `Alignment` (`tests/test_ssw.py`)
|
|
23
|
+
- Unit and end-to-end split-read rescue tests for `scanitd.inference.sr_resuer` (`tests/test_sr_resuer.py`)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## [0.9.2] — 2026-07-16
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- Filtering options were not applied (#11)
|
|
31
|
+
- Potential error in `inference/main.py` (#10)
|
|
32
|
+
- Various bug fixes across the `base`, `inference`, and `writer` modules
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- Releases now publish to PyPI via GitHub Actions using
|
|
36
|
+
[PyPI trusted publishing](https://docs.pypi.org/trusted-publishers/) (OIDC),
|
|
37
|
+
replacing manual `twine upload`
|
|
38
|
+
- Removed `poetry.lock` in favor of a single `pyproject.toml`/`uv.lock`-based
|
|
39
|
+
dependency setup
|
|
40
|
+
- Updated Python version requirements and dependencies
|
|
41
|
+
- Bumped dev dependencies: `pip` (19.2.3 → 26.1), `wheel` (0.33.6 → 0.38.1)
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- Comprehensive test suite
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## [0.9.1] — 2024-11-20
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
- Full Google-style docstrings across all modules (`base`, `inference`, `writer`, `cli`)
|
|
52
|
+
- Sphinx documentation with `autodoc`, `napoleon`, `sphinx-click`, and RTD theme
|
|
53
|
+
- User guide pages: Installation, Quick Start, Usage, Output format
|
|
54
|
+
- `sphinx-rtd-theme` and `sphinx-click` added to dev dependencies
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
- Dev dependency group reduced from 38 to 19 packages by removing redundant
|
|
58
|
+
flake8 ecosystem (replaced by ruff), `black`, `isort`, `reorder-python-imports`,
|
|
59
|
+
`pynvim`, `cohesion`, `nox`, `xdoctest`, and `safety`
|
|
60
|
+
- `[tool.isort]` config section removed from `pyproject.toml`
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## [0.9.0] — 2024-11-08
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
- Initial public release of ScanITD 2.0
|
|
68
|
+
- Chimeric-read (SA-tag) based TDUP detection
|
|
69
|
+
- Large-insertion (CIGAR-I) based TDUP/INS detection
|
|
70
|
+
- Split-read rescue via Smith-Waterman alignment (ssw-py)
|
|
71
|
+
- VCF 4.3 output with `OAO`, `AO`, `AF`, `INSSEQ`, `HOMSEQ` INFO fields
|
|
72
|
+
- Typer-based CLI with `--target` BED/region support
|
|
73
|
+
- Loguru-based structured logging
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Citation
|
|
78
|
+
|
|
79
|
+
Wang TY. and Yang R. [ScanITD: Detecting internal tandem duplication with robust
|
|
80
|
+
variant allele frequency estimation](https://doi.org/10.1093/gigascience/giaa089).
|
|
81
|
+
*GigaScience*, 2020.
|
|
@@ -13,7 +13,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "src"))
|
|
|
13
13
|
project = "ScanITD"
|
|
14
14
|
copyright = "2024, Ting-You Wang"
|
|
15
15
|
author = "Ting-You Wang"
|
|
16
|
-
release = "0.9.
|
|
16
|
+
release = "0.9.3"
|
|
17
17
|
version = "0.9"
|
|
18
18
|
|
|
19
19
|
# -- General configuration ---------------------------------------------------
|
|
@@ -32,6 +32,7 @@ file and writes results in VCF 4.3 format.
|
|
|
32
32
|
| `--depth` | `-d` | `10` | Minimum read depth at the locus |
|
|
33
33
|
| `--vaf` | `-f` | `0.1` | Minimum variant allele frequency (0–1) |
|
|
34
34
|
| `--length` | | `10` | Minimum ITD length in base pairs |
|
|
35
|
+
| `--max-depth` | | `0` | Maximum pileup read depth per position (`0` for unlimited) |
|
|
35
36
|
|
|
36
37
|
### Alignment sensitivity
|
|
37
38
|
|
|
@@ -78,7 +79,7 @@ insertions (INS).
|
|
|
78
79
|
### 3. Soft-clipped read rescue
|
|
79
80
|
|
|
80
81
|
Soft-clipped reads that lack an SA tag are aligned against the expected
|
|
81
|
-
duplicated reference sequence using Smith-Waterman
|
|
82
|
+
duplicated reference sequence using the vendored Smith-Waterman library (`scanitd.ssw`). Those
|
|
82
83
|
whose alignment is end-anchored and passes the mismatch threshold are counted
|
|
83
84
|
as additional supporting reads, improving VAF estimates.
|
|
84
85
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "scanitd"
|
|
3
|
-
version = "0.9.
|
|
3
|
+
version = "0.9.3"
|
|
4
4
|
description = "ScanITD"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Ting-You Wang", email = "tywang@northwestern.edu" },
|
|
@@ -21,7 +21,7 @@ dependencies = [
|
|
|
21
21
|
"pysam>=0.22.0",
|
|
22
22
|
"pyfaidx>=0.7.2",
|
|
23
23
|
"psutil>=5.9.5",
|
|
24
|
-
|
|
24
|
+
# ssw-py is vendored directly under src/scanitd/ssw
|
|
25
25
|
"loguru>=0.7.0",
|
|
26
26
|
"typer>=0.12.5",
|
|
27
27
|
"rich>=13.7.0",
|
|
@@ -74,6 +74,17 @@ build-backend = "hatchling.build"
|
|
|
74
74
|
|
|
75
75
|
[tool.hatch.build.targets.wheel]
|
|
76
76
|
packages = ["src/scanitd"]
|
|
77
|
+
exclude = ["*.o"]
|
|
78
|
+
|
|
79
|
+
[tool.hatch.build.hooks.custom]
|
|
80
|
+
path = "src/scanitd/ssw/build_ssw.py"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
[tool.cibuildwheel]
|
|
84
|
+
build = ["cp310-*", "cp311-*", "cp312-*", "cp313-*"]
|
|
85
|
+
skip = ["*-musllinux*", "*_i686"]
|
|
86
|
+
test-command = "scanitd --version && python -c \"from scanitd.ssw import AlignmentMgr; m = AlignmentMgr(); m.set_read('ACGT'); m.set_reference('TTACGTTT'); r = m.align(); assert r.CIGAR == '4M' and r.reference_start == 2\""
|
|
87
|
+
|
|
77
88
|
|
|
78
89
|
[tool.uv]
|
|
79
90
|
package = true
|
|
@@ -19,6 +19,14 @@ def itd_len_type(value: int) -> int:
|
|
|
19
19
|
return int(value)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
def max_depth_type(value: int) -> int:
|
|
23
|
+
"""Validate that max pileup depth is non-negative."""
|
|
24
|
+
if int(value) < 0:
|
|
25
|
+
msg = "max-depth must be non-negative (0 for unlimited)"
|
|
26
|
+
raise typer.BadParameter(msg)
|
|
27
|
+
return int(value)
|
|
28
|
+
|
|
29
|
+
|
|
22
30
|
def version_callback(value: bool):
|
|
23
31
|
"""Print the ScanITD version string and exit.
|
|
24
32
|
|
|
@@ -119,6 +127,12 @@ def main(
|
|
|
119
127
|
"--target",
|
|
120
128
|
help="Limit analysis to targets listed in the BED-format file or a samtools region string",
|
|
121
129
|
),
|
|
130
|
+
max_depth: int = typer.Option(
|
|
131
|
+
0,
|
|
132
|
+
"--max-depth",
|
|
133
|
+
callback=max_depth_type,
|
|
134
|
+
help="Maximum pileup read depth per position (0 for unlimited)",
|
|
135
|
+
),
|
|
122
136
|
log_level: LogLevel = typer.Option(LogLevel.INFO, "-l", "--log-level", help="set the logging level."),
|
|
123
137
|
version: bool | None = typer.Option(
|
|
124
138
|
None,
|
|
@@ -143,6 +157,7 @@ def main(
|
|
|
143
157
|
mismatch_sr: Maximum mismatches for soft-read rescue alignment (default: 1).
|
|
144
158
|
mismatch_insertion: Maximum mismatches for insertion-inferred duplication (default: 2).
|
|
145
159
|
target: BED file path or samtools region string to restrict analysis.
|
|
160
|
+
max_depth: Maximum pileup read depth per position; 0 means unlimited (default: 0).
|
|
146
161
|
log_level: Logging verbosity level (default: INFO).
|
|
147
162
|
version: When provided, prints version and exits.
|
|
148
163
|
"""
|
|
@@ -165,6 +180,7 @@ def main(
|
|
|
165
180
|
allowed_mismatches_for_sr_rescue=mismatch_sr,
|
|
166
181
|
allowed_mismatches_for_insertion=mismatch_insertion,
|
|
167
182
|
logger=logger,
|
|
183
|
+
max_depth=max_depth,
|
|
168
184
|
)
|
|
169
185
|
|
|
170
186
|
write_events_to_vcf(output, bam_header, event_list, logger, min_ao=ao, min_depth=dp, min_vaf=vaf)
|
|
@@ -352,11 +352,16 @@ def parse_target_genomic_coordinates(input_data):
|
|
|
352
352
|
return result
|
|
353
353
|
|
|
354
354
|
# Helper function to validate and convert coordinates
|
|
355
|
-
def validate_coordinates(chrom, start, end):
|
|
355
|
+
def validate_coordinates(chrom, start, end, *, is_bed: bool = False):
|
|
356
356
|
try:
|
|
357
357
|
start = int(start)
|
|
358
358
|
end = int(end)
|
|
359
|
-
if
|
|
359
|
+
if is_bed:
|
|
360
|
+
# BED is 0-based half-open [start, end); convert to 1-based inclusive chrom:start-end
|
|
361
|
+
if start < 0 or end < 0 or start >= end:
|
|
362
|
+
raise ValueError
|
|
363
|
+
return f"{chrom}:{start + 1}-{end}"
|
|
364
|
+
if start < 1 or end < 1 or start > end:
|
|
360
365
|
raise ValueError
|
|
361
366
|
return f"{chrom}:{start}-{end}"
|
|
362
367
|
except ValueError:
|
|
@@ -374,7 +379,7 @@ def parse_target_genomic_coordinates(input_data):
|
|
|
374
379
|
if line.strip() and not line.startswith("#"):
|
|
375
380
|
fields = line.strip().split("\t")
|
|
376
381
|
if len(fields) >= 3:
|
|
377
|
-
result.append(validate_coordinates(fields[0], fields[1], fields[2]))
|
|
382
|
+
result.append(validate_coordinates(fields[0], fields[1], fields[2], is_bed=True))
|
|
378
383
|
else:
|
|
379
384
|
msg = f"Invalid BED format in line: {line}"
|
|
380
385
|
raise ValueError(msg)
|
|
@@ -396,7 +401,7 @@ def parse_target_genomic_coordinates(input_data):
|
|
|
396
401
|
try:
|
|
397
402
|
chrom, pos = entry.split(":")
|
|
398
403
|
start, end = pos.split("-")
|
|
399
|
-
result.append(validate_coordinates(chrom, start, end))
|
|
404
|
+
result.append(validate_coordinates(chrom, start, end, is_bed=False))
|
|
400
405
|
except ValueError:
|
|
401
406
|
msg = f"Invalid chrom:start-end format: {entry}"
|
|
402
407
|
raise ValueError(msg)
|
|
@@ -405,7 +410,7 @@ def parse_target_genomic_coordinates(input_data):
|
|
|
405
410
|
elif "\t" in entry:
|
|
406
411
|
fields = entry.split("\t")
|
|
407
412
|
if len(fields) >= 3:
|
|
408
|
-
result.append(validate_coordinates(fields[0], fields[1], fields[2]))
|
|
413
|
+
result.append(validate_coordinates(fields[0], fields[1], fields[2], is_bed=True))
|
|
409
414
|
else:
|
|
410
415
|
msg = f"Invalid BED format: {entry}"
|
|
411
416
|
raise ValueError(msg)
|
|
@@ -227,6 +227,9 @@ class BamScanner:
|
|
|
227
227
|
return self.tdup_anchors
|
|
228
228
|
|
|
229
229
|
|
|
230
|
+
_PYSAM_UNLIMITED_DEPTH = 2**31 - 1
|
|
231
|
+
|
|
232
|
+
|
|
230
233
|
def scan_itd(
|
|
231
234
|
in_bam_path,
|
|
232
235
|
mapq_cutoff,
|
|
@@ -237,6 +240,7 @@ def scan_itd(
|
|
|
237
240
|
allowed_mismatches_for_insertion,
|
|
238
241
|
logger,
|
|
239
242
|
microinsertion_cutoff: int = 10,
|
|
243
|
+
max_depth: int = 0,
|
|
240
244
|
):
|
|
241
245
|
"""Run the full ScanITD detection pipeline on a BAM file.
|
|
242
246
|
|
|
@@ -258,12 +262,19 @@ def scan_itd(
|
|
|
258
262
|
logger: Logger instance implementing LoggerType.
|
|
259
263
|
microinsertion_cutoff: Maximum microinsertion length at a breakpoint
|
|
260
264
|
(default: 10).
|
|
265
|
+
max_depth: Maximum pileup read depth per position; 0 means unlimited
|
|
266
|
+
(default: 0).
|
|
261
267
|
|
|
262
268
|
Returns:
|
|
263
269
|
tuple: A 2-tuple of (sorted_event_list, bam_header) where sorted_event_list
|
|
264
270
|
is a list of :class:`~scanitd.base.Event` objects sorted by (chrom, ref_start)
|
|
265
271
|
and bam_header is the raw BAM header dict.
|
|
266
272
|
"""
|
|
273
|
+
if max_depth < 0:
|
|
274
|
+
msg = f"max_depth must be non-negative, got {max_depth}"
|
|
275
|
+
raise ValueError(msg)
|
|
276
|
+
effective_max_depth = _PYSAM_UNLIMITED_DEPTH if max_depth == 0 else max_depth
|
|
277
|
+
|
|
267
278
|
regions = parse_target_genomic_coordinates(target_file)
|
|
268
279
|
|
|
269
280
|
if len(regions) == 0:
|
|
@@ -297,7 +308,7 @@ def scan_itd(
|
|
|
297
308
|
|
|
298
309
|
for _region in regions:
|
|
299
310
|
try:
|
|
300
|
-
for pileup_column in bam_object.pileup(region=_region, stepper="all", truncate=True):
|
|
311
|
+
for pileup_column in bam_object.pileup(region=_region, stepper="all", truncate=True, max_depth=effective_max_depth):
|
|
301
312
|
# a list of pysam.PileupRead
|
|
302
313
|
for pileup_read in pileup_column.pileups:
|
|
303
314
|
position_of_pileup_site = pileup_read.query_position
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Split-read rescue functions for augmenting tandem duplication allele observations.
|
|
2
2
|
|
|
3
|
-
Uses Smith-Waterman local alignment (ssw
|
|
3
|
+
Uses vendored Smith-Waterman local alignment (scanitd.ssw) to rescue soft-clipped reads
|
|
4
4
|
that support a TDUP event but lack an SA tag, improving allele frequency estimates.
|
|
5
5
|
"""
|
|
6
6
|
|
|
@@ -8,20 +8,21 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from typing import TYPE_CHECKING
|
|
10
10
|
|
|
11
|
-
from ssw import AlignmentMgr
|
|
12
|
-
|
|
13
11
|
from scanitd.base import MappingMode
|
|
12
|
+
from scanitd.ssw import AlignmentMgr
|
|
14
13
|
|
|
15
14
|
if TYPE_CHECKING:
|
|
16
15
|
from pyfaidx import Fasta
|
|
17
16
|
|
|
17
|
+
from scanitd.base import MicroRegion
|
|
18
|
+
|
|
18
19
|
__all__ = [
|
|
19
20
|
"update_tdup_ao",
|
|
20
21
|
]
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
def update_tdup_ao(
|
|
24
|
-
tdup_id: tuple[str, int, int, str],
|
|
25
|
+
tdup_id: tuple[str, int, int, str, MicroRegion],
|
|
25
26
|
original_ao: int,
|
|
26
27
|
genome_fasta: Fasta,
|
|
27
28
|
to_be_rescued_sequences: dict,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
## ssw-py wrapper and utilities
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2017 Wyss Institute at Harvard University
|
|
4
|
+
Copyright (C) 2023 Nick Conway
|
|
5
|
+
|
|
6
|
+
The MIT License
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in
|
|
16
|
+
all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
24
|
+
THE SOFTWARE.
|
|
25
|
+
|
|
26
|
+
http://www.opensource.org/licenses/mit-license.php
|
|
27
|
+
|
|
28
|
+
## Complete-Striped-Smith-Waterman-Library (SSW C implementation)
|
|
29
|
+
https://github.com/mengyao/Complete-Striped-Smith-Waterman-Library
|
|
30
|
+
|
|
31
|
+
License: MIT
|
|
32
|
+
|
|
33
|
+
Copyright (c) 2012-2015 Boston College
|
|
34
|
+
|
|
35
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
36
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
37
|
+
in the Software without restriction, including without limitation the rights
|
|
38
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
39
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
40
|
+
furnished to do so, subject to the following conditions:
|
|
41
|
+
|
|
42
|
+
The above copyright notice and this permission notice shall be included in
|
|
43
|
+
all copies or substantial portions of the Software.
|
|
44
|
+
|
|
45
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
46
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
47
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
48
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
49
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
50
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
51
|
+
THE SOFTWARE.
|
|
52
|
+
|
|
53
|
+
Author:
|
|
54
|
+
C implementation: Mengyao Zhao
|
|
55
|
+
|
|
56
|
+
## sse2neon.h (ARM64 NEON translation header)
|
|
57
|
+
https://github.com/DLTcollab/sse2neon
|
|
58
|
+
License: MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
CC ?= gcc
|
|
2
|
+
CFLAGS = -O3 -fPIC -std=c99 -Wall -Wextra -I. $(ARCHFLAGS)
|
|
3
|
+
LDFLAGS = -lm
|
|
4
|
+
SRC = ssw.c str_util.c
|
|
5
|
+
OBJ = $(SRC:.c=.o)
|
|
6
|
+
TARGET = ssw.so
|
|
7
|
+
|
|
8
|
+
all: $(TARGET)
|
|
9
|
+
|
|
10
|
+
$(TARGET): $(OBJ)
|
|
11
|
+
$(CC) $(ARCHFLAGS) -shared -o $@ $^ $(LDFLAGS)
|
|
12
|
+
|
|
13
|
+
%.o: %.c ssw.h str_util.h
|
|
14
|
+
$(CC) $(CFLAGS) -c $< -o $@
|
|
15
|
+
|
|
16
|
+
clean:
|
|
17
|
+
rm -f $(OBJ) $(TARGET)
|