mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-19 07:33:18 +02:00
Closes the longest-standing inventory gap — finding a specific spool
in a closet of 50 partials. Per-spool icon button on every inventory
card and table row, plus a "Print labels..." header action that opens
a multi-select picker pre-loaded with the currently filtered spools.
Four pre-built templates: AMS holder (30 x 15 mm) for the popular
Makerworld AMS Filament Label Holder, single box label (62 x 29 mm)
for Brother PT/QL or Dymo small labels, Avery L7160 (A4, 21 per
sheet), and Avery 5160 (US Letter, 30 per sheet). Each label carries
the colour swatch (with multi-colour gradient stripes for spools
with extra_colors set), brand, material, name, the *spool ID*
(bsaunder's articulated user-need: telling 8 spools of "PLA White"
apart, especially partials), and a QR code that deep-links to
/inventory?spool=<id> for phone-scan round-trips. Box-label adds
storage location; AMS-holder drops the QR — at 30 x 15 mm there is
no room for swatch + text + QR without truncating away the spool ID,
and AMS-bay identification is at arm's length where the swatch and
ID are enough.
Server-side rendering via ReportLab + qrcode (already a dep). Pure
Python, no headless browser, no system libs. Output is byte-identical
across browsers, Avery sheets align to <0.1 mm, and bulk export is
one click for one PDF. Two endpoints — POST /inventory/labels (local
DB) and POST /spoolman/labels (Spoolman-backed) — gated on
INVENTORY_READ, capped at 500 spools per request, returning
application/pdf via StreamingResponse. The renderer is decoupled
from the SQLAlchemy model via a LabelData dataclass so the same code
path serves both modes.
Modal picker scales to large libraries: search (substring match
across name / brand / #ID), material filter chips derived from the
visible spools, additive Select-all-visible / Deselect-visible /
Clear-all actions so selections survive filter changes. Restyled
twice in development — first cut used generic Tailwind which clashed
with the inventory's bambu-dark palette; second cut switched to
bambu-dark-secondary / bambu-green / bambu-gray to match.
Two render bugs found during visual inspection of generated PDFs and
fixed before commit:
1. AMS-30x15 template originally produced labels with only swatch
+ QR and no text at all — the side-by-side layout left <5 mm
for the text column, so the renderer bailed without drawing
anything. Layout split into tight (h<20mm) and roomy (h>=20mm)
regimes; tight regime drops the QR and gives the right column
to brand + material + a 13pt-bold spool ID.
2. Box-62x29 template aggressively truncated text — swatch + QR
each at ~14 mm on a 26mm-tall label squeezed the text column
to ~16 mm, turning "Polymaker Ivory" into "Polymak..." and
"Polymaker . PLA . Matte" into "Polymaker ...". Swatch capped
at 16 mm, QR capped at 18 mm and constrained to ~20% of width,
leaving the text column ~30 mm — full names render without
truncation.
Both bugs pinned by regression tests in test_label_renderer.py that
render with pageCompression=0 so the resulting PDF bytes contain the
text as ASCII and `assert b"Polymaker" in pdf` works.
75 lines
1.4 KiB
Plaintext
75 lines
1.4 KiB
Plaintext
# Web Framework
|
|
fastapi>=0.109.0
|
|
uvicorn[standard]>=0.27.0
|
|
|
|
# Database
|
|
sqlalchemy>=2.0.0
|
|
aiosqlite>=0.19.0
|
|
asyncpg>=0.29.0
|
|
greenlet>=3.0.0
|
|
|
|
# Pydantic
|
|
pydantic>=2.0.0
|
|
pydantic-settings>=2.0.0
|
|
|
|
# Bambu Lab Printer Communication
|
|
paho-mqtt>=2.0.0
|
|
aioftp>=0.22.0
|
|
|
|
# Virtual Printer (emulates Bambu printer for slicer uploads)
|
|
pyftpdlib>=2.0.0
|
|
cryptography>=46.0.7
|
|
|
|
# SpoolBuddy remote SSH updates (pure-Python SSH client; avoids the
|
|
# OpenSSH `ssh` binary which calls getpwuid() and fails in Docker when
|
|
# the container UID isn't in /etc/passwd)
|
|
asyncssh>=2.18.0
|
|
|
|
# 3MF Processing (standard zipfile is sufficient for Bambu 3MF files)
|
|
defusedxml>=0.7.0 # Safe XML parsing (prevents XXE attacks)
|
|
|
|
# Excel Export
|
|
openpyxl>=3.1.0
|
|
|
|
# Notifications
|
|
pywebpush>=2.0.0
|
|
|
|
# Utilities
|
|
python-multipart>=0.0.26
|
|
aiofiles>=23.0.0
|
|
|
|
# QR Code generation
|
|
qrcode[pil]>=7.4.0
|
|
|
|
# PDF generation (spool label printing — #809)
|
|
reportlab>=4.0.0
|
|
|
|
# STL Thumbnail Generation
|
|
trimesh>=4.0.0
|
|
matplotlib>=3.8.0
|
|
fast-simplification>=0.1.0
|
|
|
|
# System monitoring
|
|
psutil>=6.0.0
|
|
|
|
# Authentication
|
|
PyJWT>=2.12.0
|
|
passlib[bcrypt]>=1.7.4
|
|
ldap3>=2.9.0
|
|
pyotp>=2.9.0
|
|
|
|
# HTTP client (used for OIDC token exchange)
|
|
httpx>=0.26.0
|
|
|
|
# Plate Detection (optional - enables build plate empty detection)
|
|
opencv-python-headless>=4.8.0
|
|
numpy>=1.24.0
|
|
|
|
# Development
|
|
pytest>=9.0.3
|
|
pytest-asyncio>=0.23.0
|
|
httpx>=0.26.0
|
|
ruff>=0.2.0
|
|
|
|
pillow>=12.2.0
|