Add restart functionality for cancelled jobs

- Add restart button in job history for cancelled jobs
- Add /job/<id>/restart route in web interface
- Add restart_job method in QueueManager to reset cancelled jobs to queued
parent 82f5cbfe
......@@ -161,6 +161,28 @@
cancelForm.appendChild(cancelBtn);
actionsElement.appendChild(cancelForm);
} else if (status === 'cancelled') {
const restartForm = document.createElement('form');
restartForm.method = 'post';
restartForm.action = `/job/${jobId}/restart`;
restartForm.style.display = 'inline';
restartForm.style.marginRight = '0.5rem';
restartForm.onsubmit = () => confirm('Are you sure you want to restart this cancelled job?');
const restartBtn = document.createElement('button');
restartBtn.type = 'submit';
restartBtn.className = 'restart-btn';
restartBtn.style.background = '#10b981';
restartBtn.style.color = 'white';
restartBtn.style.border = 'none';
restartBtn.style.padding = '0.25rem 0.5rem';
restartBtn.style.borderRadius = '4px';
restartBtn.style.fontSize = '0.8rem';
restartBtn.style.cursor = 'pointer';
restartBtn.textContent = 'Restart';
restartForm.appendChild(restartBtn);
actionsElement.appendChild(restartForm);
const deleteForm = document.createElement('form');
deleteForm.method = 'post';
deleteForm.action = `/job/${jobId}/delete`;
......@@ -281,6 +303,9 @@
<div class="job-progress">{{ job.result.get('status', 'Processing...') }}</div>
{% endif %}
{% elif job.status == 'cancelled' %}
<form method="post" action="/job/{{ job.id }}/restart" style="display: inline; margin-right: 0.5rem;" onsubmit="return confirm('Are you sure you want to restart this cancelled job?')">
<button type="submit" class="restart-btn" style="background: #10b981; color: white; border: none; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; cursor: pointer;">Restart</button>
</form>
<form method="post" action="/job/{{ job.id }}/delete" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this cancelled job?')">
<button type="submit" class="delete-btn" style="background: #dc2626; color: white; border: none; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; cursor: pointer;">Delete</button>
</form>
......
......@@ -90,6 +90,26 @@ class QueueManager:
return True
def restart_job(self, queue_id: int, user_id: int = None) -> bool:
"""Restart a cancelled job."""
# Get job status first
job = get_queue_status(queue_id)
if not job:
return False
# Check ownership if user_id provided
if user_id and job['user_id'] != user_id:
return False
# Only allow restart of cancelled jobs
if job['status'] != 'cancelled':
return False
# Reset retry_count and update status to queued
update_queue_status(queue_id, 'queued', retry_count=0)
return True
def _can_start_job(self, job: Dict[str, Any]) -> bool:
"""Check if a job can be started (worker available)."""
from .config import get_analysis_backend, get_training_backend
......
......@@ -359,6 +359,21 @@ def cancel_job(job_id):
return redirect(url_for('history'))
@app.route('/job/<int:job_id>/restart', methods=['POST'])
@login_required
def restart_job(job_id):
"""Restart a cancelled job."""
user = get_current_user_session()
from .queue import queue_manager
if queue_manager.restart_job(job_id, user['id']):
flash('Job restarted successfully!', 'success')
else:
flash('Failed to restart job or access denied.', 'error')
return redirect(url_for('history'))
@app.route('/job_result/<int:job_id>')
@login_required
def job_result(job_id):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment