Add start, stop, and finish job actions

- POST /jobs/{id}/start: modal picks from idle/offline printers,
  sets job → printing, printer → printing, logs "Job started on {name}"
- POST /jobs/{id}/stop: job → queued, clears printer_id + started_at,
  printer → idle, logs "Job stopped (was on {name})"
- POST /jobs/{id}/finish: job → done, sets finished_at + duration_minutes,
  printer → idle, logs "Job finished on {name}"
- Job detail shows Start / Stop+Finish buttons based on current status
- All printers currently printing are excluded from the Start printer picker

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Martin Hohenberg
2026-06-18 22:45:46 +02:00
parent bc6e06a2e2
commit 6df42800ee
2 changed files with 125 additions and 0 deletions

View File

@@ -260,6 +260,9 @@ def job_detail(job_id: int, request: Request, db: Session = Depends(get_db)):
"materials": available_materials() or MATERIALS,
"colors": available_colors() or COLORS,
"all_statuses": [s.value for s in JobStatus],
"available_printers": db.query(Printer).filter(
Printer.status != PrinterStatus.printing
).order_by(Printer.name).all(),
})
@@ -294,6 +297,68 @@ def edit_job(
return RedirectResponse(url=f"/jobs/{job_id}", status_code=HTTP_303_SEE_OTHER)
@router.post("/jobs/{job_id}/start")
def start_job(job_id: int, printer_id: int = Form(...), db: Session = Depends(get_db)):
from fastapi import HTTPException
job = db.query(PrintJob).filter(PrintJob.id == job_id).first()
if not job:
raise HTTPException(404, "Job not found")
printer = db.query(Printer).filter(Printer.id == printer_id).first()
if not printer:
raise HTTPException(404, "Printer not found")
job.status = JobStatus.printing
job.printer_id = printer_id
job.started_at = datetime.now()
printer.status = PrinterStatus.printing
db.add(JobLog(job_id=job_id, message=f"Job started on {printer.name}"))
db.commit()
return RedirectResponse(url=f"/jobs/{job_id}", status_code=HTTP_303_SEE_OTHER)
@router.post("/jobs/{job_id}/stop")
def stop_job(job_id: int, db: Session = Depends(get_db)):
from fastapi import HTTPException
job = db.query(PrintJob).filter(PrintJob.id == job_id).first()
if not job:
raise HTTPException(404, "Job not found")
printer_name = job.printer.name if job.printer else "unknown printer"
if job.printer:
job.printer.status = PrinterStatus.idle
job.status = JobStatus.queued
job.printer_id = None
job.started_at = None
db.add(JobLog(job_id=job_id, message=f"Job stopped (was on {printer_name})"))
db.commit()
return RedirectResponse(url=f"/jobs/{job_id}", status_code=HTTP_303_SEE_OTHER)
@router.post("/jobs/{job_id}/finish")
def finish_job(job_id: int, db: Session = Depends(get_db)):
from fastapi import HTTPException
job = db.query(PrintJob).filter(PrintJob.id == job_id).first()
if not job:
raise HTTPException(404, "Job not found")
now = datetime.now()
job.status = JobStatus.done
job.finished_at = now
if job.started_at:
job.duration_minutes = max(1, int((now - job.started_at).total_seconds() / 60))
printer_name = job.printer.name if job.printer else "unknown printer"
if job.printer:
job.printer.status = PrinterStatus.idle
db.add(JobLog(job_id=job_id, message=f"Job finished on {printer_name}"))
db.commit()
return RedirectResponse(url=f"/jobs/{job_id}", status_code=HTTP_303_SEE_OTHER)
@router.post("/jobs/{job_id}/analyse")
def analyse_job_file(job_id: int, db: Session = Depends(get_db)):
from fastapi import HTTPException