From 6df42800ee781133bdc16198c0d3cc26b8ac1f4b Mon Sep 17 00:00:00 2001 From: Martin Hohenberg Date: Thu, 18 Jun 2026 22:45:46 +0200 Subject: [PATCH] Add start, stop, and finish job actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- app/routers/ui.py | 65 +++++++++++++++++++++++++++++++++++ app/templates/job_detail.html | 60 ++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/app/routers/ui.py b/app/routers/ui.py index 3bfde9e..b325139 100644 --- a/app/routers/ui.py +++ b/app/routers/ui.py @@ -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 diff --git a/app/templates/job_detail.html b/app/templates/job_detail.html index c2bbaaa..29e1820 100644 --- a/app/templates/job_detail.html +++ b/app/templates/job_detail.html @@ -25,6 +25,29 @@ +{# ── Job actions ── #} +{% set s = job.status.value %} +{% if s == 'queued' %} +
+ +
+{% elif s == 'printing' %} +
+
+ +
+
+ +
+
+{% endif %} +
{# ── Job info ── #} @@ -312,4 +335,41 @@
+ +{# ── Start Job Modal ── #} +{% if job.status.value == 'queued' %} + +{% endif %} {% endblock %}