Job list: sort by status/priority/age, hide old finished jobs by default

Sort order: printing → queued → rest, then priority desc, then created_at asc.
Finished jobs (done/failed/cancelled) older than 24h are hidden by default;
a footer note shows the count with a 'Show all' link (?show_all=1).
Filter params (bracket_id, q) are preserved on the show-all link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Martin Hohenberg
2026-06-18 22:35:36 +02:00
parent 6c60dc5960
commit 4a3c370317
2 changed files with 38 additions and 3 deletions

View File

@@ -1,11 +1,12 @@
import os
import uuid as _uuid
from datetime import datetime, timedelta
from pathlib import Path
from fastapi import APIRouter, Depends, File, Form, Request, UploadFile
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy import or_
from sqlalchemy import case, or_
from sqlalchemy.orm import Session
from starlette.status import HTTP_303_SEE_OTHER
@@ -127,7 +128,7 @@ def create_printer_type(
# ── Jobs ───────────────────────────────────────────────────────────────────────
@router.get("/jobs", response_class=HTMLResponse)
def jobs_page(request: Request, q: str = "", bracket_id: str = "", db: Session = Depends(get_db)):
def jobs_page(request: Request, q: str = "", bracket_id: str = "", show_all: str = "", db: Session = Depends(get_db)):
query = db.query(PrintJob)
if q:
like = f"%{q}%"
@@ -138,11 +139,35 @@ def jobs_page(request: Request, q: str = "", bracket_id: str = "", db: Session =
if bracket_id:
query = query.filter(PrintJob.bracket_id == int(bracket_id))
active_bracket = db.query(JobBracket).filter(JobBracket.id == int(bracket_id)).first()
# Hide finished jobs older than 24h unless show_all
finished = [JobStatus.done, JobStatus.failed, JobStatus.cancelled]
cutoff = datetime.now() - timedelta(hours=24)
hidden_count = 0
if not show_all:
hidden_count = query.filter(
PrintJob.status.in_(finished), PrintJob.created_at < cutoff
).count()
query = query.filter(
or_(~PrintJob.status.in_(finished), PrintJob.created_at >= cutoff)
)
# Sort: printing → queued → rest, then priority, then oldest first
status_order = case(
(PrintJob.status == JobStatus.printing, 0),
(PrintJob.status == JobStatus.queued, 1),
else_=2,
)
jobs = query.order_by(status_order, PrintJob.priority.desc(), PrintJob.created_at.asc()).all()
return templates.TemplateResponse("jobs.html", {
"request": request,
"active": "jobs",
"jobs": query.order_by(PrintJob.priority.desc(), PrintJob.created_at.desc()).all(),
"jobs": jobs,
"q": q,
"bracket_id": bracket_id,
"show_all": bool(show_all),
"hidden_count": hidden_count,
"active_bracket": active_bracket,
"printers": db.query(Printer).order_by(Printer.name).all(),
"brackets": db.query(JobBracket).order_by(JobBracket.name).all(),

View File

@@ -93,6 +93,16 @@
{% endif %}
</div>
{% if hidden_count %}
{% set sep = '&' if (q or bracket_id) else '?' %}
{% set show_all_url = '/jobs?show_all=1' ~ ('&bracket_id=' ~ bracket_id if bracket_id else '') ~ ('&q=' ~ q if q else '') %}
<p class="text-secondary small mt-3 mb-0">
<i class="bi bi-archive me-1"></i>
{{ hidden_count }} finished job{{ 's' if hidden_count != 1 else '' }} older than 24 h hidden.
<a href="{{ show_all_url }}" class="text-secondary">Show all</a>
</p>
{% endif %}
{# ── Add Job Modal ── #}
<div class="modal fade" id="addJobModal" tabindex="-1">
<div class="modal-dialog modal-lg">