Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
bf49808
Design spec: clone verification, peak analysis accuracy, UI refresh
adrobi Oct 24, 2026
ab05234
Implementation plan: clone verification, peak accuracy, UI refresh, CLI
adrobi Oct 24, 2026
2cd6d19
Test corpus: real sangeranalyseR reads, COI and pUC19 references
adrobi Oct 24, 2026
2954b65
Test corpus: per-sample COI references for divergent lineages
adrobi Oct 24, 2026
236340f
peaks: normalised secondary-peak analysis at any read position
adrobi Oct 24, 2026
ac89e13
Fix untranslated verify notes, Q=0 mean for reads without quality, re…
adrobi Oct 24, 2026
faabf63
reference: load constructs from FASTA, GenBank and SnapGene
adrobi Oct 24, 2026
474b04e
tests: well-formed LOCUS line in reference test
adrobi Oct 24, 2026
20706df
align: circular constructs with origin-spanning reads
adrobi Oct 29, 2026
1437a42
align: count circular alternatives distinct modulo construct length
adrobi Oct 29, 2026
6f4d061
clones: group reads into clones by file name
adrobi Oct 29, 2026
923ce4d
clones: keep a lone well-shaped token as the clone name
adrobi Oct 29, 2026
ced2057
clones: per-clone coverage, merged differences and verdicts
adrobi Oct 29, 2026
a46c619
align: count distinct placements; clones: ambiguity by placements
adrobi Oct 29, 2026
58510f6
effects: amino-acid consequences of clone differences in CDS
adrobi Oct 29, 2026
514cd4c
clone_report: CSV and Excel reports for clone verification
adrobi Oct 29, 2026
0c040b3
project: .ab1proj files for clone verification sessions
adrobi Oct 29, 2026
e438777
ui: background tasks with progress and cancel
adrobi Oct 30, 2026
2fdde1b
ui: tasks cancel on close, last-start-wins per key, safe on destroyed…
adrobi Oct 30, 2026
df1f57c
ui: split main window into read_view modules (no behaviour change)
adrobi Oct 30, 2026
b118e60
ui: ReadView owns undo/redo actions; broaden read_edited tests
adrobi Oct 30, 2026
cf0423e
ui: QSS theme, icons, unified status palette, coloured quality bars
adrobi Oct 30, 2026
6f89ae5
ui: checkbox ticks in channel colours, theme-aware status cells, weak…
adrobi Oct 30, 2026
a7ca545
ui: construct summary bar and clone matrix
adrobi Oct 30, 2026
90d35cd
ui: matrix review fixes: cached name width, out-of-region marks, holl…
adrobi Oct 30, 2026
f2c0b60
ui: difference detail pane with stacked chromatograms
adrobi Oct 30, 2026
03e0282
ui: detail pane review fixes: disputed colour, dimmed out-of-region r…
adrobi Oct 30, 2026
94be6c3
ui: detail pane survives corrupt ab1 (AssertionError/IndexError)
adrobi Oct 30, 2026
eb9377e
ui: construct verification workspace with grouping and project files
adrobi Oct 30, 2026
f836f42
ui: construct workspace review fixes: inactive viewer actions, clear …
adrobi Oct 30, 2026
faa2c07
CLI subcommands verify-clones/qc/export; docs and v1.1.0
adrobi Oct 30, 2026
022d969
CLI review fixes: console code page, early -o check, README exit-code…
adrobi Oct 30, 2026
77ec9d3
CLI: console code page fallback to utf-8, all-or-nothing console attach
adrobi Oct 30, 2026
2296531
App icon: SVG source, PNG/ICO, window and exe icon
adrobi Oct 30, 2026
e6165d1
App icon tests: compare decoded pixels, not bytes
adrobi Oct 30, 2026
12c770f
Release workflow: Windows and Linux builds with smoke test, single re…
adrobi Oct 30, 2026
8a549c3
Clone verification: merge deletion runs into one difference (in-frame…
adrobi Oct 30, 2026
c05bd45
verify-clones: report unusable reads (stderr, read_issues column); di…
adrobi Oct 30, 2026
0048eaa
Tests: timing tests behind a perf marker, bounds scaled by AB1KIT_PER…
adrobi Oct 30, 2026
87d96b2
Project files: a malformed grouping is a ValueError
adrobi Oct 30, 2026
a1a4494
Grouping: natural sort uses isdecimal (no crash on superscript digits)
adrobi Oct 30, 2026
39be495
Theme: no tick/arrow image instead of a crash when TEMP is not writable
adrobi Oct 30, 2026
d358584
Detail pane: an index past an edited read counts as not covered
adrobi Oct 30, 2026
5d3d95a
CI: Linux test leg (ubuntu-22.04, Python 3.13); release trigger note;…
adrobi Oct 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,21 @@ jobs:
python-version: ${{ matrix.python-version }}
- run: python -m pip install -e ".[dev]"
- run: python -m pytest -q

tests-linux:
runs-on: ubuntu-22.04
env:
QT_QPA_PLATFORM: offscreen
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install Qt runtime libraries
run: |
sudo apt-get update
sudo apt-get install -y libegl1 libxkbcommon-x11-0 libxcb-cursor0 \
libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 \
libxcb-render-util0 libxcb-shape0 libxcb-xinerama0 libxcb-xfixes0 libgl1
- run: python -m pip install -e ".[dev]"
- run: python -m pytest -q
106 changes: 99 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
name: Release

# workflow_dispatch builds and uploads the artifacts; a release is created only for v* tags.
on:
push:
tags: ["v*"]
workflow_dispatch:

jobs:
windows-exe:
runs-on: windows-latest
build:
name: build (${{ matrix.artifact }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
artifact: ab1kit-windows
file: dist/ab1kit.exe
- os: ubuntu-22.04
artifact: ab1kit-linux-x86_64
file: ab1kit-linux-x86_64.tar.gz
permissions:
contents: write
contents: read
env:
QT_QPA_PLATFORM: offscreen
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install Qt runtime libraries (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libegl1 libxkbcommon-x11-0 libxcb-cursor0 \
libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 \
libxcb-render-util0 libxcb-shape0 libxcb-xinerama0 libxcb-xfixes0 libgl1
- run: python -m pip install -e ".[dev]" "pyinstaller==6.22.3"
- name: Run tests
run: python -m pytest -q

- name: Build exe (errors surfaced as annotations)
if: runner.os == 'Windows'
shell: pwsh
run: |
python -m PyInstaller ab1kit.spec --noconfirm --workpath build-pyinstaller 2>&1 |
Expand All @@ -26,13 +51,80 @@ jobs:
Write-Host "::error::PyInstaller failed, log tail: $tail"
exit 1
}
- name: Build binary (errors surfaced as annotations)
if: runner.os == 'Linux'
shell: bash
run: |
set -o pipefail
if ! python -m PyInstaller ab1kit.spec --noconfirm --workpath build-pyinstaller 2>&1 | tee build_log.txt; then
tail_log=$(tail -n 30 build_log.txt | tr '\n' '|')
echo "::error::PyInstaller failed, log tail: ${tail_log}"
exit 1
fi

- name: Smoke-test frozen exe (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force smoke/reads | Out-Null
Copy-Item tests/data/real/Achl_ACHLO006-09_*.ab1 smoke/reads/
# The exe is windowed: Start-Process -Wait -PassThru is needed to get the exit code.
$p = Start-Process -FilePath dist/ab1kit.exe -Wait -PassThru -ArgumentList @(
"verify-clones", "--construct", "tests/data/refs/coi_HM417896.gb",
"smoke/reads", "-o", "smoke/report.xlsx")
if ($p.ExitCode -ne 0) {
Write-Host "::error::smoke test: ab1kit.exe verify-clones exited with $($p.ExitCode)"
exit 1
}
if (-not (Test-Path smoke/report.xlsx)) {
Write-Host "::error::smoke test: smoke/report.xlsx was not created"
exit 1
}
- name: Smoke-test frozen binary (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
mkdir -p smoke/reads
cp tests/data/real/Achl_ACHLO006-09_*.ab1 smoke/reads/
rc=0
dist/ab1kit verify-clones --construct tests/data/refs/coi_HM417896.gb \
smoke/reads -o smoke/report.xlsx || rc=$?
if [ "$rc" -ne 0 ]; then
echo "::error::smoke test: dist/ab1kit verify-clones exited with $rc"
exit 1
fi
if [ ! -f smoke/report.xlsx ]; then
echo "::error::smoke test: smoke/report.xlsx was not created"
exit 1
fi

- name: Package tarball (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
cp README.md LICENSE dist/
tar -czf ab1kit-linux-x86_64.tar.gz -C dist ab1kit README.md LICENSE

- uses: actions/upload-artifact@v4
with:
name: ab1kit-windows
path: dist/ab1kit.exe
name: ${{ matrix.artifact }}
path: ${{ matrix.file }}
if-no-files-found: error

release:
needs: build
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create GitHub release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
files: dist/ab1kit.exe
files: |
artifacts/ab1kit-windows/ab1kit.exe
artifacts/ab1kit-linux-x86_64/ab1kit-linux-x86_64.tar.gz
generate_release_notes: true
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,44 @@ All notable changes to ab1kit are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning is
[SemVer](https://semver.org/).

## [1.1.0] — 2026-10-30

Clone verification: check a plate of clones against a construct, in the GUI or
from the command line.

### Added
- Reference loading from FASTA, GenBank and SnapGene files, with circular
constructs and reads spanning the origin
- Normalised secondary-peak analysis at any read position
- Grouping of reads into clones by file name (default rules or a custom regex),
per-clone coverage by strand, merged differences and verdicts
(`ok` / `errors` / `incomplete` / `mixed` / `no_data`)
- Amino-acid consequences of differences inside CDS features
- Clone report as CSV and Excel (sheets `Clones` and `Differences`; unusable
reads listed per clone in `read_issues` and on the CLI's stderr);
`.ab1proj` project files for verification sessions
- Clone-verification workspace: summary bar, clone matrix, difference detail pane with stacked
chromatograms, grouping editor, drag-and-drop of reads and projects
- Background tasks with progress and cancel
- Command-line subcommands `verify-clones`, `qc` and `export` (no Qt import)
- Real-data test corpus (sangeranalyseR reads, COI and pUC19 references)
- Linux release build (`ab1kit-linux-x86_64.tar.gz`) alongside the Windows exe, built and smoke-tested by GitHub Actions
- Application icon (window and Windows exe)

### Changed
- Refreshed UI: QSS theme, icons, one status palette everywhere
- Quality colour thresholds are now <20 / 20-29 / >=30 everywhere
- Main window split into the `ui.read_view` package (no behaviour change)
- Alignment counts distinct placements; repeated hits are flagged as ambiguous

### Fixed
- Reverse-strand reads in expected-SNP verification and the reference overlay
were indexed in the wrong orientation (now correct)
- Reverse-complementing a read misplaced peak positions; editing sessions saved
after a reverse-complement with 1.0.0 or earlier hold wrong peak positions —
re-open the original file and reverse-complement again
- Untranslated verification notes; mean quality for reads without quality data

## [1.0.0] — 2026-10-24

First feature-complete release: the full cycle «open a chromatogram → inspect →
Expand Down
89 changes: 83 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# ab1kit
# <img src="ui/icons/app/ab1kit.png" width="48" align="center"> ab1kit

Open-source desktop tool for viewing, editing and analyzing Sanger sequencing
chromatograms (`.ab1` files) — a free, offline alternative to FinchTV / Chromas
for everyday molecular biology lab work. English/Russian interface.

![viewer](docs/screenshots/viewer.png)
![construct check](docs/screenshots/construct.png)

## Features

Expand Down Expand Up @@ -48,6 +49,73 @@ for everyday molecular biology lab work. English/Russian interface.
![dark theme](docs/screenshots/viewer-dark.png)
![batch mode](docs/screenshots/batch.png)

## Clone verification

Check a set of plasmid or amplicon clones against the expected construct from
their Sanger reads: open the clone-verification workspace, load the reference (FASTA,
GenBank or SnapGene), drop the `.ab1` reads of the whole plate, optionally set
the region of interest, and read the verdict per clone off the colour matrix.

![clone verification](docs/screenshots/construct.png)

**Scenario.** Load the construct, add the reads (files or a folder), check the
automatic grouping into clones, choose the region; every clone gets its verdict right away. Click a
difference in the matrix to see the stacked chromatograms of every read at that
position. Export the result as Excel/CSV, or save the whole session as an
`.ab1proj` file. FASTA constructs are treated as circular (reads may span the
origin); GenBank/SnapGene files take the topology from the file; the *Circular*
checkbox (`--linear` on the command line) overrides the topology.

**File names.** Reads are grouped by name as `<clone>_<primer>`: the primer is
the last known primer token (`F`, `R`, `T7`, `SP6`, `M13F`, `pUC-F`, ...); if
the name has none, the last token is taken as the primer. The clone is what
precedes the primer, minus a single-digit order token and a leading plate well
(`A05`), the well being stripped only when another clone token follows it
(`B2_F` is clone `B2`). `A05_clone3_2_R.ab1` is clone `clone3`, primer `R`;
`Achl_ACHLO006-09_1_F.ab1` is clone `Achl_ACHLO006-09`. Files whose name yields
no clone stay unassigned and are skipped. For other conventions give a regular
expression with named groups `clone` and `primer` that matches the whole file
name without the extension, e.g. `(?P<clone>\d+)-(?P<primer>[A-Za-z0-9]+)`.

**Verdicts.** Checked in this order, the first match wins:

| Verdict | Meaning |
| --- | --- |
| `no_data` (grey) | no read of the clone aligned to the construct |
| `mixed` (purple) | at least 3 positions (default) with a double peak: probably a mixed colony |
| `errors` (red) | at least one difference in the region that is confirmed or disputed |
| `incomplete` (yellow) | no such difference, but part of the region is not covered by any read |
| `ok` (green) | the region is fully covered and shows no reliable difference |

A difference is *confirmed* (only supporting reads, Q >= min Q), *disputed*
(another read shows the reference there) or *low quality* (seen only below
min Q). Both confirmed and disputed differences give `errors`; only low-quality
ones do not change the verdict. Defaults: min Q 20, double-peak ratio 0.40.

**Command line** (no GUI, no Qt needed):

```
ab1kit verify-clones --construct pET28-gene.gb --region 120-1850 reads/ -o report.xlsx
ab1kit qc reads/ -o qc.csv
ab1kit export reads/ --trim --fasta -o trimmed/
```

`verify-clones` prints a table of clones and exits with 0 if at least one clone
is `ok`, 1 if none is, 2 on invalid input; `-o report.csv` writes `report.csv`
and `report_differences.csv`. The `differences` count (table and report) is the
in-region differences that are not low quality, i.e. those behind `errors`.
Reads that could not be used (unreadable, not aligned, ambiguous placement) are
listed on stderr as `ab1kit: <file>: <reason>` and in the `read_issues` column. Options: `--region A-B` (1-based, inclusive),
`--linear`, `--pattern REGEX`, `--min-q N`, `--het-ratio X`.

The exit-code contract applies to the `ab1kit` command installed with pip. The
Windows release `ab1kit.exe` is a windowed program: the shell returns to the
prompt immediately, so exit codes and output ordering are unreliable and output
may appear after the prompt. Wait for it explicitly: `start /wait ab1kit.exe
verify-clones ...` (cmd), or `Start-Process -Wait -NoNewWindow` /
`(Start-Process ab1kit.exe -ArgumentList ... -Wait -PassThru).ExitCode`
(PowerShell).

## Getting started

Requires Python 3.11+:
Expand All @@ -56,12 +124,20 @@ Requires Python 3.11+:
python -m venv .venv
.venv/Scripts/pip install -e ".[dev]" # Windows; on POSIX: .venv/bin/pip
.venv/Scripts/python -m pytest # run the test suite
.venv/Scripts/python -m pytest -m perf # timing tests (skipped by default)
.venv/Scripts/ab1kit # launch the GUI
```

`ab1kit` also accepts a file or folder argument. On Windows, a prebuilt
executable is available on the [Releases](https://github.com/adrobi/ab1kit/releases)
page.
`ab1kit` also accepts a file or folder argument. Prebuilt binaries are on the
[Releases](https://github.com/adrobi/ab1kit/releases) page:

- **Windows:** `ab1kit.exe` (single file, no installation).
- **Linux (x86_64):** `ab1kit-linux-x86_64.tar.gz`. Unpack with
`tar -xzf ab1kit-linux-x86_64.tar.gz` and run `./ab1kit`. Built on Ubuntu
22.04, so it needs glibc 2.35 or newer. The GUI needs the usual Qt runtime
libraries; on Debian/Ubuntu:
`sudo apt install libegl1 libxkbcommon-x11-0 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xinerama0 libxcb-xfixes0 libgl1 libfontconfig1 libdbus-1-3 libglib2.0-0 libxcb-xkb1 libxcb-xinput0 libxcb-shm0`.
The Linux binary prints CLI output to the terminal normally.

## Keyboard shortcuts

Expand Down Expand Up @@ -96,5 +172,6 @@ Example chromatograms in `examples/` come from the

## Status

v1.0.0 — viewer, editing, trimming, batch mode, alignment, SNP calling and
expected-SNP verification are all implemented. See [CHANGELOG.md](CHANGELOG.md).
v1.1.0 — adds clone verification, a command-line interface, a refreshed UI
and more accurate peak analysis on top of the 1.0 viewer, editing, batch mode,
alignment, SNP calling and expected-SNP verification. See [CHANGELOG.md](CHANGELOG.md).
15 changes: 12 additions & 3 deletions ab1kit.spec
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
# -*- mode: python ; coding: utf-8 -*-
"""PyInstaller spec for the ab1kit one-file Windows executable.
"""PyInstaller spec for the ab1kit one-file executable (Windows exe, Linux ELF).

Build: pyinstaller ab1kit.spec --noconfirm
"""

import sys

a = Analysis(
["chrom_cli.py"],
pathex=[],
binaries=[],
datas=[],
hiddenimports=["chrom_core", "ui", "i18n", "translations_en"],
datas=[
("ui/style.qss", "ui"),
("ui/style_dark.qss", "ui"),
("ui/icons", "ui/icons"),
],
hiddenimports=["chrom_core", "ui", "ui.read_view", "ui.construct", "ui.status", "ui.tables", "i18n", "translations_en"],
hookspath=[],
excludes=["tkinter", "matplotlib", "numpy.tests"],
noarchive=False,
Expand All @@ -26,5 +32,8 @@ exe = EXE(
debug=False,
strip=False,
upx=False,
# console=False only matters on Windows/macOS; a Linux ELF keeps stdout.
console=False,
# .ico is a Windows/macOS-only option; PyInstaller does not use it on Linux.
icon="ui/icons/app/ab1kit.ico" if sys.platform == "win32" else None,
)
Loading
Loading