feat: improve batch task controls and release 1.2.0
This commit is contained in:
1 parent
0e13bb5c29
commit
84e183d28d
38 files changed
+632
-147
No files matched your search
@@ -3,11 +3,13 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
from uuid import UUID
|
||||
|
||||
from apps.admin_console.core.state import state
|
||||
from apps.admin_console.services.automation_store import store, new_id
|
||||
from apps.admin_console.services.task_import import case_goal
|
||||
from apps.admin_console.services.task_import import case_goal, case_title
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TERMINAL = {"completed", "failed", "cancelled", "skipped", "interrupted"}
|
||||
@@ -82,9 +84,9 @@ async def on_terminal(item: dict, status: str):
|
||||
job = store.job(sid)
|
||||
if job is None:
|
||||
raise RuntimeError("任务元数据写入失败")
|
||||
job.update(status="finalizing", ended_at=time.time())
|
||||
if job.get("timed_out"):
|
||||
status = "failed"
|
||||
job.update(status="finalizing", terminal_status=status, ended_at=time.time())
|
||||
store.save_job(job)
|
||||
try:
|
||||
job["status"] = status
|
||||
@@ -99,7 +101,9 @@ async def on_terminal(item: dict, status: str):
|
||||
}
|
||||
):
|
||||
job["status"] = "failed"
|
||||
job["error"] = "任务执行结束,但业务检查未通过"
|
||||
job["error"] = job.get("error") or "任务执行结束,但业务检查未通过"
|
||||
elif job["status"] == "failed" and report.get("error") and not job.get("error"):
|
||||
job["error"] = report["error"]
|
||||
# Persist terminal classification before export, but the coordinator waits for finalization.
|
||||
store.save_job({**job, "status": "finalizing", "terminal_status": job["status"]})
|
||||
await asyncio.to_thread(export_session, sid, job["formats"])
|
||||
@@ -107,6 +111,7 @@ async def on_terminal(item: dict, status: str):
|
||||
except Exception as exc:
|
||||
logger.exception("Report finalization failed for %s", sid)
|
||||
job["report_error"] = str(exc)[:500]
|
||||
job.pop("terminal_status", None)
|
||||
store.save_job(job)
|
||||
|
||||
|
||||
@@ -138,7 +143,9 @@ async def tick_batches():
|
||||
and not job.get("timed_out")
|
||||
and time.time() - job.get("started_at", time.time()) - excluded > limit
|
||||
):
|
||||
job.update(error="任务达到批次配置的执行时限", timed_out=True)
|
||||
job.update(
|
||||
error=f"任务执行超时({limit} 秒,不含暂停/排队时间)", timed_out=True
|
||||
)
|
||||
store.save_job(job)
|
||||
task_queue_service.stop_tasks(session_id=job["session_id"])
|
||||
batch.update(status="paused", reason="任务超时,已暂停剩余任务;请确认设备状态")
|
||||
@@ -202,6 +209,7 @@ async def tick_batches():
|
||||
runtime_overrides=options,
|
||||
session_id=job["session_id"],
|
||||
ingress="batch",
|
||||
display_name=case_title(job["case"]),
|
||||
)
|
||||
if response.get("status") == "rejected" or not response.get("tasks"):
|
||||
raise ValueError(response.get("error") or "队列拒绝接收任务")
|
||||
@@ -265,6 +273,35 @@ def create_batch(payload: dict) -> dict:
|
||||
return store.create_batch(batch, jobs, payload)
|
||||
|
||||
|
||||
def delete_batch(batch_id: str) -> bool:
|
||||
"""Delete an inactive batch, its sessions and its generated reports."""
|
||||
from artemis.data_engine.storage import StorageManager
|
||||
from apps.admin_console.services import report_export
|
||||
|
||||
jobs = store.begin_delete_batch(batch_id)
|
||||
if jobs is None:
|
||||
return False
|
||||
storage = StorageManager(store.path, report_export.TRACES_PATH)
|
||||
mcp_exports = report_export.EXPORT_ROOT.parent / "mcp_exports"
|
||||
with report_export.EXPORT_LOCK:
|
||||
for job in jobs:
|
||||
session_id = str(UUID(job["session_id"]))
|
||||
storage.delete_session(UUID(session_id))
|
||||
report_dir = report_export.EXPORT_ROOT / "sessions" / session_id
|
||||
if report_dir.is_dir():
|
||||
shutil.rmtree(report_dir)
|
||||
mcp_session_dir = mcp_exports / "sessions" / session_id
|
||||
if mcp_session_dir.is_dir():
|
||||
shutil.rmtree(mcp_session_dir)
|
||||
batch_dir = report_export.EXPORT_ROOT / "batches" / batch_id
|
||||
if batch_dir.is_dir():
|
||||
shutil.rmtree(batch_dir)
|
||||
mcp_batch_dir = mcp_exports / "batches" / batch_id
|
||||
if mcp_batch_dir.is_dir():
|
||||
shutil.rmtree(mcp_batch_dir)
|
||||
return store.delete_batch(batch_id)
|
||||
|
||||
|
||||
def batch_action(batch_id: str, action: str) -> dict:
|
||||
batch = store.get("batch", batch_id)
|
||||
if not batch:
|
||||
|
||||
Reference in new issue
Block a user