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>
67 lines
1.8 KiB
Python
67 lines
1.8 KiB
Python
import logging
|
|
import os
|
|
import time
|
|
|
|
import httpx
|
|
|
|
logger = logging.getLogger("pops")
|
|
|
|
_cache: dict[str, tuple[float, list]] = {}
|
|
_TTL = 300 # seconds
|
|
|
|
|
|
def _base() -> str:
|
|
return os.environ.get("FILAMENTDB_URL", "https://web.filamentdb.orb.local").rstrip("/")
|
|
|
|
|
|
def _get(path: str, params: dict | None = None) -> list | dict:
|
|
r = httpx.get(f"{_base()}{path}", params=params, timeout=5, verify=False)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
|
|
def _cached(key: str, fetch):
|
|
now = time.monotonic()
|
|
if key in _cache and now - _cache[key][0] < _TTL:
|
|
return _cache[key][1]
|
|
try:
|
|
result = fetch()
|
|
_cache[key] = (now, result)
|
|
return result
|
|
except Exception:
|
|
logger.warning("FilamentDB unreachable — using cached/fallback data")
|
|
return _cache.get(key, (0, []))[1]
|
|
|
|
|
|
def get_stock() -> list[dict]:
|
|
"""All filament types with stock counts from /reports/stock."""
|
|
return _cached("stock", lambda: _get("/reports/stock"))
|
|
|
|
|
|
def available_materials() -> list[str]:
|
|
"""Unique materials that have at least one roll in stock."""
|
|
rows = get_stock()
|
|
seen, out = set(), []
|
|
for r in rows:
|
|
m = r.get("material", "")
|
|
if m and r.get("rolls_in_stock", 0) > 0 and m not in seen:
|
|
seen.add(m)
|
|
out.append(m)
|
|
return sorted(out)
|
|
|
|
|
|
def available_colors(material: str | None = None) -> list[str]:
|
|
"""Unique color names in stock, optionally filtered by material."""
|
|
rows = get_stock()
|
|
seen, out = set(), []
|
|
for r in rows:
|
|
if r.get("rolls_in_stock", 0) <= 0:
|
|
continue
|
|
if material and r.get("material") != material:
|
|
continue
|
|
c = r.get("color_name") or ""
|
|
if c and c not in seen:
|
|
seen.add(c)
|
|
out.append(c)
|
|
return sorted(out)
|