from __future__ import annotations from backend.app.api.errors import AppError from backend.app.api.schemas import TaskDetailResponse, TaskListItem, TaskListResponse from backend.app.db.history_repository import HistoryRepository from backend.app.db.task_repository import TaskRepository FILE_ACTION_CANCELLABLE_OPERATIONS = {"copy", "move", "duplicate", "delete"} class TaskService: def __init__(self, repository: TaskRepository, history_repository: HistoryRepository | None = None): self._repository = repository self._history_repository = history_repository def create_task(self, operation: str, source: str, destination: str) -> TaskDetailResponse: task = self._repository.create_task(operation=operation, source=source, destination=destination) return TaskDetailResponse(**task) def get_task(self, task_id: str) -> TaskDetailResponse: task = self._repository.get_task(task_id) if not task: raise AppError( code="task_not_found", message="Task was not found", status_code=404, details={"task_id": task_id}, ) return TaskDetailResponse(**task) def list_tasks(self) -> TaskListResponse: tasks = self._repository.list_tasks() return TaskListResponse( items=[ TaskListItem( id=task["id"], operation=task["operation"], status=task["status"], source=task["source"], destination=task["destination"], created_at=task["created_at"], finished_at=task["finished_at"], ) for task in tasks ] ) def cancel_task(self, task_id: str) -> TaskDetailResponse: task = self._repository.get_task(task_id) if not task: raise AppError( code="task_not_found", message="Task was not found", status_code=404, details={"task_id": task_id}, ) if task["operation"] not in FILE_ACTION_CANCELLABLE_OPERATIONS: raise AppError( code="task_not_cancellable", message="Task cannot be cancelled", status_code=409, details={"task_id": task_id, "status": task["status"]}, ) if task["status"] not in {"queued", "running", "cancelling"}: raise AppError( code="task_not_cancellable", message="Task cannot be cancelled", status_code=409, details={"task_id": task_id, "status": task["status"]}, ) updated = self._repository.request_cancellation(task_id) if not updated: raise AppError( code="task_not_cancellable", message="Task cannot be cancelled", status_code=409, details={"task_id": task_id, "status": task["status"]}, ) if updated["status"] == "cancelled" and self._history_repository: self._history_repository.update_entry(entry_id=task_id, status="cancelled") return TaskDetailResponse(**updated)