Skip to content

Commit 54b8364

Browse files
committed
fixed vcf raw counts reporting bug
1 parent f0d7d68 commit 54b8364

3 files changed

Lines changed: 28 additions & 21 deletions

File tree

‎Dockerfile‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,23 @@ COPY --from=builder /wheels /wheels
4444
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
4545

4646
# Install snpEff (optional, makes image larger)
47-
ARG INSTALL_SNPEFF=false
47+
ARG INSTALL_SNPEFF=true
4848
RUN if [ "$INSTALL_SNPEFF" = "true" ]; then \
4949
apt-get update && apt-get install -y --no-install-recommends \
5050
default-jre-headless \
5151
wget \
5252
unzip \
53-
&& wget -q https://snpeff.blob.core.windows.net/versions/snpEff_latest_core.zip \
53+
&& wget -q https://snpeff-public.s3.amazonaws.com/versions/snpEff_latest_core.zip \
5454
&& unzip snpEff_latest_core.zip -d /opt \
5555
&& rm snpEff_latest_core.zip \
56-
&& ln -s /opt/snpEff/exec/snpEff /usr/local/bin/snpEff \
5756
&& apt-get remove -y wget unzip \
5857
&& apt-get autoremove -y \
5958
&& rm -rf /var/lib/apt/lists/*; \
6059
fi
60+
ENV PATH="/opt/snpEff/exec:${PATH}"
61+
RUN if [ -f /opt/snpEff/exec/snpeff ]; then \
62+
ln -s /opt/snpEff/exec/snpeff /opt/snpEff/exec/snpEff; \
63+
fi
6164

6265
# Create non-root user
6366
RUN useradd -m -s /bin/bash bsaseq

‎src/bsaseq/cli.py‎

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
)
2626
from bsaseq.analysis.windows import calculate_windows
2727
from bsaseq.io.summary import AnalysisSummary, write_summary
28-
from bsaseq.io.vcf import get_sample_names, parse_vcf, write_candidates_vcf
28+
from bsaseq.io.vcf import get_sample_names, parse_vcf_to_list, write_candidates_vcf
2929
from bsaseq.io.writers import (
3030
write_annotated_candidates_tsv,
3131
write_candidate_genes_tsv,
@@ -360,16 +360,14 @@ def run(
360360

361361
try:
362362
# Parse VCF and collect variants
363-
variants = list(
364-
parse_vcf(
365-
vcf_path=vcf,
366-
high_bulk=high_bulk,
367-
low_bulk=low_bulk,
368-
min_dp=min_dp,
369-
max_dp=max_dp,
370-
min_gq=min_gq,
371-
min_qual=min_qual,
372-
)
363+
variants, vcf_stats = parse_vcf_to_list(
364+
vcf_path=vcf,
365+
high_bulk=high_bulk,
366+
low_bulk=low_bulk,
367+
min_dp=min_dp,
368+
max_dp=max_dp,
369+
min_gq=min_gq,
370+
min_qual=min_qual,
373371
)
374372

375373
if not variants:
@@ -619,8 +617,8 @@ def run(
619617
vcf_path=str(vcf),
620618
high_bulk_samples=high_samples,
621619
low_bulk_samples=low_samples,
622-
total_variants=len(variants), # We don't have pre-filter count easily
623-
passing_variants=len(variants),
620+
total_variants=vcf_stats.total_records,
621+
passing_variants=vcf_stats.passed,
624622
total_windows=len(windows),
625623
significant_windows=significant_windows,
626624
candidate_regions=len(regions),

‎src/bsaseq/io/vcf.py‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def parse_vcf(
129129
max_dp: int = 200,
130130
min_gq: int = 20,
131131
min_qual: float = 30.0,
132+
_stats: FilterStats | None = None,
132133
) -> Iterator[Variant]:
133134
"""Parse VCF and yield Variant objects.
134135
@@ -194,7 +195,7 @@ def parse_vcf(
194195
f"min_gq={min_gq}, min_qual={min_qual}"
195196
)
196197

197-
stats = FilterStats()
198+
stats = _stats if _stats is not None else FilterStats()
198199

199200
for record in vcf:
200201
stats.total_records += 1
@@ -323,8 +324,8 @@ def parse_vcf_to_list(
323324
max_dp: int = 200,
324325
min_gq: int = 20,
325326
min_qual: float = 30.0,
326-
) -> list[Variant]:
327-
"""Parse VCF and return list of Variant objects.
327+
) -> tuple[list[Variant], FilterStats]:
328+
"""Parse VCF and return list of Variant objects with filter statistics.
328329
329330
Convenience wrapper around parse_vcf() that returns a list
330331
instead of an iterator. Use with caution for large VCF files
@@ -340,13 +341,16 @@ def parse_vcf_to_list(
340341
min_qual: Minimum variant QUAL score.
341342
342343
Returns:
343-
List of Variant objects passing all filters.
344+
Tuple of (variants, stats) where variants is a list of Variant
345+
objects passing all filters and stats is the FilterStats with
346+
counts for all records including those filtered out.
344347
345348
Raises:
346349
ValueError: If sample names not found in VCF.
347350
FileNotFoundError: If VCF file does not exist.
348351
"""
349-
return list(
352+
stats = FilterStats()
353+
variants = list(
350354
parse_vcf(
351355
vcf_path=vcf_path,
352356
high_bulk=high_bulk,
@@ -355,8 +359,10 @@ def parse_vcf_to_list(
355359
max_dp=max_dp,
356360
min_gq=min_gq,
357361
min_qual=min_qual,
362+
_stats=stats,
358363
)
359364
)
365+
return variants, stats
360366

361367

362368
def write_candidates_vcf(

0 commit comments

Comments
 (0)