Remove internal filament management, integrate FilamentDB, add job detail page
Filament removal:
- Deleted app/models/filament.py, routers/filaments.py, templates/filaments.html
- Migration 0009: drops filaments table and filament_id FK from print_jobs
- Removed Filaments from sidebar and all internal references
FilamentDB integration (read-only):
- app/filamentdb.py: GET /reports/stock with 5-min in-memory cache
- available_materials() / available_colors() populate job form dropdowns
- Falls back to static constants if FilamentDB unreachable
- Dashboard filament count now from FilamentDB rolls_in_stock
- FILAMENTDB_URL added to .env.install; httpx added to requirements
Job detail page (/jobs/{id}):
- Two cards: Job info (customer, material, printer, bracket, timing, cost)
and File & Geometry (bounding box + volume from stl_cache)
- Log timeline with Add Entry modal
- Filename in jobs list links to detail page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.db import get_db
|
||||
from app.models.filament import Filament
|
||||
|
||||
router = APIRouter(prefix="/filaments", tags=["filaments"])
|
||||
|
||||
|
||||
@router.get("/")
|
||||
def list_filaments(db: Session = Depends(get_db)):
|
||||
return [f.to_dict() for f in db.query(Filament).all()]
|
||||
|
||||
|
||||
@router.get("/{filament_id}")
|
||||
def get_filament(filament_id: int, db: Session = Depends(get_db)):
|
||||
filament = db.query(Filament).filter(Filament.id == filament_id).first()
|
||||
if not filament:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=404, detail="Filament not found")
|
||||
return filament.to_dict()
|
||||
@@ -9,8 +9,8 @@ from sqlalchemy.orm import Session
|
||||
from starlette.status import HTTP_303_SEE_OTHER
|
||||
|
||||
from app.db import get_db
|
||||
from app.models.filament import Filament
|
||||
from app.models.job_bracket import JobBracket
|
||||
from app.models.stl_cache import StlCache
|
||||
from app.models.job_log import JobLog
|
||||
from app.models.print_job import JobStatus, PrintJob
|
||||
from app.models.printer import Printer, PrinterStatus
|
||||
@@ -19,6 +19,7 @@ from app.models.printer_type import PrinterType
|
||||
from app.models.pricing import PricingConfig
|
||||
from app.models.todo import Todo
|
||||
from app.constants import COLORS, MATERIALS
|
||||
from app.filamentdb import available_colors, available_materials, get_stock
|
||||
from app.stl import get_stl_dir, list_stl_files
|
||||
from app.stl_analysis import analyse, get_cache_map
|
||||
|
||||
@@ -40,7 +41,7 @@ def dashboard(request: Request, db: Session = Depends(get_db)):
|
||||
"active_jobs": db.query(PrintJob).filter(
|
||||
PrintJob.status.in_([JobStatus.queued, JobStatus.printing])
|
||||
).count(),
|
||||
"total_filaments": db.query(Filament).count(),
|
||||
"total_filaments": sum(r.get("rolls_in_stock", 0) for r in get_stock()),
|
||||
"recent_jobs": db.query(PrintJob).order_by(PrintJob.created_at.desc()).limit(5).all(),
|
||||
})
|
||||
|
||||
@@ -141,8 +142,8 @@ def jobs_page(request: Request, q: str = "", db: Session = Depends(get_db)):
|
||||
"brackets": db.query(JobBracket).order_by(JobBracket.name).all(),
|
||||
"stl_files": (stl_files := list_stl_files()),
|
||||
"stl_cache": get_cache_map(stl_files, db),
|
||||
"materials": MATERIALS,
|
||||
"colors": COLORS,
|
||||
"materials": available_materials() or MATERIALS,
|
||||
"colors": available_colors() or COLORS,
|
||||
})
|
||||
|
||||
|
||||
@@ -205,6 +206,28 @@ async def create_job(
|
||||
return RedirectResponse(url="/jobs", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
|
||||
@router.get("/jobs/{job_id}", response_class=HTMLResponse)
|
||||
def job_detail(job_id: int, request: Request, db: Session = Depends(get_db)):
|
||||
from fastapi import HTTPException
|
||||
job = db.query(PrintJob).filter(PrintJob.id == job_id).first()
|
||||
if not job:
|
||||
raise HTTPException(status_code=404, detail="Job not found")
|
||||
stl = db.query(StlCache).filter_by(filename=job.file_name).first() if job.file_name else None
|
||||
return templates.TemplateResponse("job_detail.html", {
|
||||
"request": request,
|
||||
"active": "jobs",
|
||||
"job": job,
|
||||
"stl": stl,
|
||||
})
|
||||
|
||||
|
||||
@router.post("/jobs/{job_id}/log")
|
||||
def add_job_log(job_id: int, message: str = Form(...), db: Session = Depends(get_db)):
|
||||
db.add(JobLog(job_id=job_id, message=message.strip()))
|
||||
db.commit()
|
||||
return RedirectResponse(url=f"/jobs/{job_id}", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
|
||||
# ── Brackets ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@router.get("/brackets", response_class=HTMLResponse)
|
||||
@@ -241,8 +264,8 @@ def bracket_detail(bracket_id: int, request: Request, db: Session = Depends(get_
|
||||
"filaments": db.query(Filament).order_by(Filament.material, Filament.color).all(),
|
||||
"stl_files": (stl_files := list_stl_files()),
|
||||
"stl_cache": get_cache_map(stl_files, db),
|
||||
"materials": MATERIALS,
|
||||
"colors": COLORS,
|
||||
"materials": available_materials() or MATERIALS,
|
||||
"colors": available_colors() or COLORS,
|
||||
})
|
||||
|
||||
|
||||
@@ -283,17 +306,6 @@ async def add_jobs_to_bracket(
|
||||
return RedirectResponse(url=f"/brackets/{bracket_id}", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
|
||||
# ── Filaments ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@router.get("/filaments", response_class=HTMLResponse)
|
||||
def filaments_page(request: Request, db: Session = Depends(get_db)):
|
||||
return templates.TemplateResponse("filaments.html", {
|
||||
"request": request,
|
||||
"active": "filaments",
|
||||
"filaments": db.query(Filament).order_by(Filament.brand, Filament.material).all(),
|
||||
})
|
||||
|
||||
|
||||
# ── To-Do ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
@router.get("/todos", response_class=HTMLResponse)
|
||||
|
||||
Reference in New Issue
Block a user