docs: update README and add pixi-based tests
- Add property-based tests for PixiRunner - Add HAN055.fna test data file - Update README with pixi installation and usage guide - Update .gitignore for pixi and test artifacts - Update CLI to remove Docker-related arguments
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Bttoxin Pipeline API (pixi-based).
|
||||
|
||||
This module provides the API for running the BtToxin pipeline using pixi environments:
|
||||
- digger environment: BtToxin_Digger with bioconda dependencies
|
||||
- pipeline environment: Python analysis with pandas/matplotlib/seaborn
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -10,16 +16,15 @@ from types import SimpleNamespace
|
||||
from typing import Dict, Any, Optional
|
||||
import sys as _sys
|
||||
|
||||
# Ensure repo-relative imports for backend and scripts when running from installed package
|
||||
# Ensure repo-relative imports for scripts when running from installed package
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
_BACKEND_DIR = _REPO_ROOT / "backend"
|
||||
_SCRIPTS_DIR = _REPO_ROOT / "scripts"
|
||||
for _p in (str(_BACKEND_DIR), str(_SCRIPTS_DIR)):
|
||||
for _p in (str(_SCRIPTS_DIR),):
|
||||
if _p not in _sys.path:
|
||||
_sys.path.append(_p)
|
||||
|
||||
# Import DockerContainerManager from backend
|
||||
from app.utils.docker_client import DockerContainerManager # type: ignore
|
||||
# Import PixiRunner from scripts
|
||||
from pixi_runner import PixiRunner # type: ignore
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -45,21 +50,19 @@ def _lazy_import_plotter():
|
||||
|
||||
|
||||
class BtToxinRunner:
|
||||
"""Wrap BtToxin_Digger docker invocation for a single FNA."""
|
||||
"""Wrap BtToxin_Digger pixi invocation for a single FNA."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
image: str = "quay.io/biocontainers/bttoxin_digger:1.0.10--hdfd78af_0",
|
||||
platform: str = "linux/amd64",
|
||||
base_workdir: Optional[Path] = None,
|
||||
bttoxin_db_dir: Optional[Path] = None,
|
||||
) -> None:
|
||||
self.image = image
|
||||
self.platform = platform
|
||||
if base_workdir is None:
|
||||
base_workdir = _REPO_ROOT / "runs" / "bttoxin"
|
||||
self.base_workdir = base_workdir
|
||||
self.base_workdir.mkdir(parents=True, exist_ok=True)
|
||||
self.mgr = DockerContainerManager(image=self.image, platform=self.platform)
|
||||
self.bttoxin_db_dir = bttoxin_db_dir
|
||||
self.runner = PixiRunner(pixi_project_dir=_REPO_ROOT, env_name="digger")
|
||||
|
||||
def _prepare_layout(self, fna_path: Path) -> tuple[Path, Path, Path, Path, str]:
|
||||
if not fna_path.exists():
|
||||
@@ -86,13 +89,14 @@ class BtToxinRunner:
|
||||
fna_path = Path(fna_path)
|
||||
input_dir, digger_out, log_dir, run_root, sample_name = self._prepare_layout(fna_path)
|
||||
logger.info("Start BtToxin_Digger: %s (sample=%s)", fna_path, sample_name)
|
||||
result = self.mgr.run_bttoxin_digger(
|
||||
result = self.runner.run_bttoxin_digger(
|
||||
input_dir=input_dir,
|
||||
output_dir=digger_out,
|
||||
log_dir=log_dir,
|
||||
sequence_type=sequence_type,
|
||||
scaf_suffix=fna_path.suffix or ".fna",
|
||||
threads=threads,
|
||||
bttoxin_db_dir=self.bttoxin_db_dir,
|
||||
)
|
||||
toxins_dir = digger_out / "Results" / "Toxins"
|
||||
files = {
|
||||
@@ -234,15 +238,13 @@ class PlotAPI:
|
||||
|
||||
|
||||
class BtSingleFnaPipeline:
|
||||
"""End-to-end single-FNA pipeline: Digger → Shotter → Plot → Bundle."""
|
||||
"""End-to-end single-FNA pipeline: Digger → Shotter → Plot → Bundle (pixi-based)."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
image: str = "quay.io/biocontainers/bttoxin_digger:1.0.10--hdfd78af_0",
|
||||
platform: str = "linux/amd64",
|
||||
base_workdir: Optional[Path] = None,
|
||||
) -> None:
|
||||
self.digger = BtToxinRunner(image=image, platform=platform, base_workdir=base_workdir)
|
||||
self.base_workdir = base_workdir
|
||||
self.shotter = ShotterAPI()
|
||||
self.plotter = PlotAPI()
|
||||
|
||||
@@ -256,8 +258,11 @@ class BtSingleFnaPipeline:
|
||||
require_index_hit: bool = False,
|
||||
lang: str = "zh",
|
||||
threads: int = 4,
|
||||
bttoxin_db_dir: Optional[Path] = None,
|
||||
) -> Dict[str, Any]:
|
||||
dig = self.digger.run_single_fna(fna_path=fna, sequence_type="nucl", threads=threads)
|
||||
# Create digger runner with optional external database
|
||||
digger = BtToxinRunner(base_workdir=self.base_workdir, bttoxin_db_dir=bttoxin_db_dir)
|
||||
dig = digger.run_single_fna(fna_path=fna, sequence_type="nucl", threads=threads)
|
||||
if not dig.get("success"):
|
||||
return {"ok": False, "stage": "digger", "detail": dig}
|
||||
run_root: Path = dig["run_root"]
|
||||
|
||||
Reference in New Issue
Block a user