Refactor: Unified pipeline execution, simplified UI, and fixed Docker config
- Backend: Refactored tasks.py to directly invoke run_single_fna_pipeline.py for consistency. - Backend: Changed output format to ZIP and added auto-cleanup of intermediate files. - Backend: Fixed language parameter passing in API and tasks. - Frontend: Removed CRISPR Fusion UI elements from Submit and Monitor views. - Frontend: Implemented simulated progress bar for better UX. - Frontend: Restored One-click load button and added result file structure documentation. - Docker: Fixed critical Restarting loop by removing incorrect image directive in docker-compose.yml. - Docker: Optimized Dockerfile to correct .pixi environment path issues and prevent accidental deletion of frontend assets.
This commit is contained in:
@@ -1,19 +1,4 @@
|
||||
from celery import Celery
|
||||
from .config import settings
|
||||
|
||||
|
||||
celery_app = Celery(
|
||||
"bttoxin",
|
||||
broker=settings.get_celery_broker_url(),
|
||||
backend=settings.get_celery_result_backend(),
|
||||
)
|
||||
|
||||
celery_app.conf.update(
|
||||
task_track_started=True,
|
||||
worker_prefetch_multiplier=1,
|
||||
)
|
||||
|
||||
"""Celery 配置"""
|
||||
"""Celery Configuration"""
|
||||
from celery import Celery
|
||||
from ..config import settings
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
"""Docker 客户端管理"""
|
||||
import docker
|
||||
from typing import Dict, Any
|
||||
from pathlib import Path
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class DockerManager:
|
||||
"""Docker 容器管理器"""
|
||||
|
||||
def __init__(self, image: str = None):
|
||||
from ..config import settings
|
||||
self.client = docker.from_env()
|
||||
self.image = image or settings.DOCKER_IMAGE
|
||||
|
||||
def ensure_image(self) -> bool:
|
||||
"""确保镜像存在"""
|
||||
try:
|
||||
self.client.images.get(self.image)
|
||||
return True
|
||||
except docker.errors.ImageNotFound:
|
||||
logger.info(f"Pulling image {self.image}...")
|
||||
self.client.images.pull(self.image)
|
||||
return True
|
||||
|
||||
def run_bttoxin_digger(
|
||||
self,
|
||||
input_dir: Path,
|
||||
output_dir: Path,
|
||||
sequence_type: str = "nucl",
|
||||
scaf_suffix: str = ".fna",
|
||||
threads: int = 4
|
||||
) -> Dict[str, Any]:
|
||||
"""运行 BtToxin_Digger"""
|
||||
self.ensure_image()
|
||||
|
||||
volumes = {
|
||||
str(input_dir.absolute()): {'bind': '/data', 'mode': 'ro'},
|
||||
str(output_dir.absolute()): {'bind': '/results', 'mode': 'rw'}
|
||||
}
|
||||
|
||||
command = [
|
||||
"/usr/local/env-execute", "BtToxin_Digger",
|
||||
"--SeqPath", "/data",
|
||||
"--SequenceType", sequence_type,
|
||||
"--Scaf_suffix", scaf_suffix,
|
||||
"--threads", str(threads)
|
||||
]
|
||||
|
||||
try:
|
||||
container = self.client.containers.run(
|
||||
self.image,
|
||||
command=command,
|
||||
volumes=volumes,
|
||||
platform="linux/amd64",
|
||||
detach=True,
|
||||
remove=False
|
||||
)
|
||||
|
||||
result = container.wait()
|
||||
logs = container.logs().decode('utf-8')
|
||||
container.remove()
|
||||
|
||||
return {
|
||||
'success': result['StatusCode'] == 0,
|
||||
'logs': logs,
|
||||
'exit_code': result['StatusCode']
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error: {e}")
|
||||
return {'success': False, 'error': str(e)}
|
||||
73
backend/app/core/tool_runner.py
Normal file
73
backend/app/core/tool_runner.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""本地工具运行器 - 替代 Docker 调用"""
|
||||
import subprocess
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ToolRunner:
|
||||
"""本地工具运行管理器"""
|
||||
|
||||
def run_bttoxin_digger(
|
||||
self,
|
||||
input_dir: Path,
|
||||
output_dir: Path,
|
||||
sequence_type: str = "nucl",
|
||||
scaf_suffix: str = ".fna",
|
||||
threads: int = 4
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
在本地环境运行 BtToxin_Digger
|
||||
使用 'pixi run -e digger' 激活环境
|
||||
"""
|
||||
# Ensure output directory exists
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# BtToxin_Digger expects to read files from --SeqPath.
|
||||
# It scans the directory for files matching *suffix.
|
||||
# We point it to the input directory.
|
||||
seq_path = input_dir.absolute()
|
||||
|
||||
# Construct command
|
||||
# Note: We run this from output_dir so 'Results/' are created there.
|
||||
command = [
|
||||
"pixi", "run", "-e", "digger", "BtToxin_Digger",
|
||||
"--SeqPath", str(seq_path),
|
||||
"--SequenceType", sequence_type,
|
||||
"--Scaf_suffix", scaf_suffix,
|
||||
"--threads", str(threads)
|
||||
]
|
||||
|
||||
logger.info(f"Running command: {' '.join(command)}")
|
||||
logger.info(f"Working directory: {output_dir}")
|
||||
|
||||
try:
|
||||
# Run subprocess
|
||||
# Capture both stdout and stderr
|
||||
process = subprocess.run(
|
||||
command,
|
||||
cwd=str(output_dir),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
env=os.environ.copy() # Inherit env vars (PATH, etc)
|
||||
)
|
||||
|
||||
success = (process.returncode == 0)
|
||||
logs = f"STDOUT:\n{process.stdout}\n\nSTDERR:\n{process.stderr}"
|
||||
|
||||
if not success:
|
||||
logger.error(f"BtToxin_Digger failed with exit code {process.returncode}")
|
||||
logger.error(process.stderr)
|
||||
|
||||
return {
|
||||
'success': success,
|
||||
'logs': logs,
|
||||
'exit_code': process.returncode
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Execution error: {e}")
|
||||
return {'success': False, 'error': str(e)}
|
||||
Reference in New Issue
Block a user