Files
spoolman2/CLAUDE.md
tonym 9365e399be
Some checks failed
CI / style (push) Has been cancelled
CI / build-client (push) Has been cancelled
CI / build-amd64 (push) Has been cancelled
CI / build-tester (push) Has been cancelled
CI / test (cockroachdb) (push) Has been cancelled
CI / test (mariadb) (push) Has been cancelled
CI / test (postgres) (push) Has been cancelled
CI / test (sqlite) (push) Has been cancelled
CI / build-arm64 (push) Has been cancelled
CI / build-armv7 (push) Has been cancelled
CI / publish-images (push) Has been cancelled
CI / publish-release (push) Has been cancelled
Add Print Queue UI and slicer post-processing script
- Create Print Queue page showing pending/completed/cancelled jobs
- Add needs_weighing filter to spool API for flagged spools
- Add IPrintJob interface and needs_weighing to ISpool model
- Add slicer_post_process.py for Elegoo/Orca Slicer integration
- Add translations for print queue feature
- Bump version to 0.23C.3

The workflow:
1. Slicer post-processing script creates pending print job
2. After print, user confirms (deducts filament) or cancels (flags for weighing)
3. Spools needing weigh-in are shown in Print Queue UI

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 21:40:22 -06:00

14 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Spoolman is a self-hosted web service for managing 3D printer filament spools. It tracks inventory, monitors usage, and integrates with OctoPrint and Klipper/Moonraker.

Tech Stack:

  • Backend: Python 3.9-3.12, FastAPI, SQLAlchemy 2.0 (async), Alembic
  • Frontend: React 19, TypeScript, Vite, Ant Design, Refine framework
  • Databases: SQLite (default), PostgreSQL, MySQL, CockroachDB

Common Commands

Python (PDM)

pdm install                    # Install all dependencies including dev
pdm sync --prod --no-editable  # Install production dependencies only
pdm run app                    # Run backend server (uvicorn)
pdm run itest                  # Run integration tests (all databases)
pdm run docs                   # Generate API documentation

Frontend

cd client
npm ci                         # Install dependencies
npm run dev                    # Development server with hot reload
npm run build                  # Production build
npm run tsc                    # Type check only

Linting/Formatting

black .                        # Format Python code
ruff check . --fix             # Lint and auto-fix Python
pre-commit run --all-files     # Run all pre-commit hooks

Integration Tests

Tests run in Docker containers against multiple database types:

python tests_integration/run.py sqlite          # Single database
python tests_integration/run.py sqlite postgres # Multiple databases
pdm run itest                                   # All databases

Run a single test:

pytest tests_integration/tests/spool/test_add.py::test_add_spool_remaining_weight -v

Database Migrations

  1. Edit models in spoolman/database/models.py
  2. Run the server to update local SQLite
  3. pdm run alembic revision -m "description" --autogenerate
  4. Review generated migration, format with Black/Ruff

Architecture

Backend (/spoolman/)

  • main.py - FastAPI app entry, startup/shutdown, SPA mounting
  • env.py - Environment variable parsing and configuration
  • ws.py - WebSocket manager for real-time updates
  • api/v1/ - REST endpoints and WebSocket handlers
    • router.py - Main router with WebSocket root endpoint
    • spool.py, filament.py, vendor.py - CRUD endpoints per resource
    • models.py - Pydantic response models
  • database/ - SQLAlchemy models and async database operations
    • models.py - ORM definitions (Vendor, Filament, Spool, Setting, *Field)
    • database.py - Connection setup, backup logic, migrations

Frontend (/client/src/)

  • App.tsx - Routing and Refine framework setup
  • pages/ - Page components (spools/, filaments/, vendors/, settings/)
  • components/dataProvider.ts - REST API integration
  • components/liveProvider.ts - WebSocket live updates

Data Model

Vendor (1) ──→ (many) Filament (1) ──→ (many) Spool

All entities support custom fields via *Field tables (VendorField, FilamentField, SpoolField).

API Patterns

  • REST: Standard CRUD at /api/v1/{resource}
  • WebSocket: Real-time events at WS /api/v1/ or WS /api/v1/{resource}
  • Query params: ?skip=0&limit=50&sort_by=name&sort_order=asc

Key Configuration

Environment variables (see .env.example):

  • SPOOLMAN_DB_TYPE - Database type (sqlite/mysql/postgres/cockroachdb)
  • SPOOLMAN_PORT - Server port (default: 7912)
  • SPOOLMAN_DEBUG_MODE - Enables permissive CORS
  • VITE_APIURL - Frontend API base URL (e.g., /api/v1)

Code Quality

  • Line length: 120 characters
  • Python target: 3.9
  • Pre-commit hooks enforce Black formatting and Ruff linting
  • Backend is fully async using SQLAlchemy AsyncSession

Gitea Repository

This is a fork with UX improvements. Issues are tracked on the Gitea instance above.

UX Improvement Issues

# Title Status
1 Inline Creation Modals DONE
2 Smart Density Defaults DONE
3 QR Code Sizing Fixes DONE
4 Layout Max-Width DONE
5 Temperature Ranges DONE
6 Spool Adjustment History DONE
7 3dfilamentprofiles Integration DONE
8 (see Gitea #8) DONE
9 Hierarchical Locations (Room/Bin) Open
10 Database Import/Export DONE
11 Theme Colors DONE
12 Clickable Dashboard Cards DONE
13 QR Code Size Setting DONE
14 Parse Temps from Comments DONE
15 Print Job Tracking / Slicer Integration DONE
16 Prev/Next Navigation on Edit Pages DONE

Issue #1 Complete - Inline Creation Modals

Added ability to create filament/vendor inline without leaving the page:

  • client/src/components/vendorCreateModal.tsx - Quick vendor creation
  • client/src/components/filamentCreateModal.tsx - Quick filament creation with nested vendor modal
  • Modified spool create to add "Create Filament" in dropdown
  • Modified filament create to add "Create Manufacturer" in dropdown
  • Uses dropdownRender pattern (same as location select)
  • Uses useInvalidate to refresh lists after creation

Issue #2 Complete - Smart Density Defaults

Auto-fill density based on material type:

  • client/src/utils/materialDefaults.ts - Density lookup table with fuzzy matching
  • Material field converted to AutoComplete (dropdown suggestions + free text)
  • Fuzzy matching: "PLA Silk" → PLA (1.24 g/cm³)
  • New filaments default to 1.75mm diameter and 1.24 g/cm³ (PLA)
  • Applied to both full create page and FilamentCreateModal
  • Edit page not affected (preserves user customizations)

Material densities in lookup:

PLA: 1.24, ABS: 1.04, PETG: 1.27, NYLON: 1.52, TPU: 1.21
PC: 1.3, WOOD: 1.28, CF: 1.3, PC/ABS: 1.19, HIPS: 1.03
PVA: 1.23, ASA: 1.05, PP: 0.9, POM: 1.4, PMMA: 1.18, FPE: 2.16

Issue #3 Complete - QR Code Sizing Fixes

See upstream issue: https://github.com/Donkie/Spoolman/issues/671

Changes:

  • Removed fixed 50% max-width constraint (now uses fit-content by default)
  • Added qrCodePadding setting (0-10mm, default 2mm)
  • Added qrCodeMaxWidth setting (0-100%, 0 = auto)
  • Added UI controls with slider + input number for both settings

Files modified:

  • client/src/pages/printing/printing.tsx - Added settings to interface
  • client/src/pages/printing/qrCodePrintingDialog.tsx - CSS and UI controls

Issue #4 Complete - Layout Max-Width

Changes:

  • Added 1400px max-width container in SpoolmanLayout
  • Added 800px max-width for forms via CSS override in overrides.css
  • Content is centered on wide screens

Issue #5 Complete - Temperature Ranges

Store extruder and bed temperatures as min/max ranges:

  • Added 4 new DB columns: settings_extruder_temp_min/max, settings_bed_temp_min/max
  • Old single-value columns kept for backward compatibility (deprecated)
  • Alembic migration copies existing values to both min and max
  • Frontend uses Space.Compact with min/max input pairs
  • Label template shows ranges: "ET: 190-220 °C"

Files:

  • spoolman/database/models.py - New columns
  • migrations/versions/2025_01_15_0200-*.py - Migration
  • spoolman/api/v1/models.py, filament.py - API changes
  • client/src/pages/filaments/create.tsx - Form updates
  • client/src/pages/printing/spoolQrCodePrintingDialog.tsx - Template tags

Issue #6 Complete - Spool Adjustment History

Track history of all spool weight/length adjustments with timestamps and comments.

Backend:

  • spoolman/database/models.py - Added SpoolAdjustment model with relationship to Spool
  • migrations/versions/2025_01_15_0300-*.py - Migration for spool_adjustment table
  • spoolman/api/v1/models.py - Added SpoolAdjustment Pydantic model
  • spoolman/api/v1/spool.py - Added comment field to use/measure endpoints, GET /spool/{id}/adjustments endpoint
  • spoolman/database/spool.py - Updated use_weight, use_length, measure functions to record adjustments

Frontend:

  • client/src/pages/spools/model.tsx - Added ISpoolAdjustment interface
  • client/src/pages/spools/show.tsx - Collapsible adjustment history table
  • client/public/locales/en/common.json - Translation keys for history table

Issue #7 Complete - 3dfilamentprofiles Integration

Added 3dfilamentprofiles.com as a second external database source alongside SpoolmanDB.

Backend (spoolman/externaldb.py):

  • Added ExternalSource enum (SPOOLMANDB, FILAMENT_PROFILES_3D)
  • Added Filament3DFP model for raw 3dfp data
  • Added _transform_3dfp_to_external() to convert 3dfp format to ExternalFilament
  • Updated _sync() to fetch from both sources and merge results
  • Environment variables: EXTERNAL_DB_3DFP_URL, EXTERNAL_DB_3DFP_ENABLED

Frontend:

  • client/src/utils/queryExternalDB.ts - Added ExternalSource enum, source field, temp range fields
  • client/src/components/filamentImportModal.tsx - Added tabs to filter by source (All/SpoolmanDB/3DFP)
  • client/src/pages/filaments/create.tsx - Updated import handler for temp range fields

Data mapping (3dfp → Spoolman):

  • brand_namemanufacturer
  • color + material_typename
  • rgbcolor_hex (strip # prefix)
  • properties.temp_min/maxextruder_temp/extruder_temp_max
  • properties.bed_temp_min/maxbed_temp/bed_temp_max
  • Diameter defaults to 1.75mm (3dfp doesn't provide)
  • Density from material lookup table

Issue #10 Complete - Database Import/Export

Full backup/restore functionality for migrating between databases (PostgreSQL ↔ SQLite).

Backend (spoolman/api/v1/backup.py):

  • GET /api/v1/backup/export - Downloads all data as single JSON file
  • POST /api/v1/backup/import - Imports JSON with replace option
  • Exports: vendors, filaments, spools, spool_adjustments, settings
  • Handles ID remapping to maintain relationships
  • Requires python-multipart package for file uploads

Frontend (client/src/pages/settings/generalSettings.tsx):

  • "Backup & Restore" section in Settings page
  • "Download Backup" button for export
  • File upload with "Import (Merge)" or "Import (Replace All)" options
  • Confirmation dialog for destructive replace operation

Migration workflow:

  1. Export from PostgreSQL instance via Settings → Download Backup
  2. Reconfigure container for SQLite
  3. Import via Settings → Import (Replace All)

Local Development Setup

To run locally against the Unraid PostgreSQL database:

# Create virtualenv and install deps
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[postgres]"

# Start backend (reads .env file)
source .venv/bin/activate
export $(grep -v '^#' .env | xargs)
uvicorn spoolman.main:app --host 0.0.0.0 --port 7912

# Start frontend (separate terminal)
cd client
VITE_APIURL=http://localhost:7912/api/v1 npm run dev

Access at http://localhost:5173

Issue #11 Complete - Theme Colors

Added theme color selector with 5 color options:

  • client/src/contexts/color-mode/index.tsx - Theme color system with THEME_COLORS constant
  • Colors: orange, teal, red, green, blue
  • Persists in localStorage
  • Settings page has color selector buttons

Issue #12 Complete - Clickable Dashboard Cards

Dashboard cards now link to their respective list pages:

  • client/src/pages/home/index.tsx - Card body is clickable, links to list
  • Only the + button stays independent (for create)

Issue #13 Complete - QR Code Size Setting

Added explicit QR code size control:

  • client/src/pages/printing/printing.tsx - Added qrCodeSize to interface
  • client/src/pages/printing/qrCodePrintingDialog.tsx - Slider for size in mm (0=auto)

Issue #15 Complete - Print Job Tracking / Slicer Integration

Track pending print jobs from slicer and manage filament deduction after prints complete.

Workflow:

  1. Slicer post-processing script parses G-code for filament usage
  2. Script creates pending print job in Spoolman via API
  3. After print completes, user visits Print Queue UI
  4. Complete: Deducts filament from spool, records adjustment history
  5. Cancel: Flags spool for manual weigh-in (no automatic deduction)

Backend:

  • spoolman/database/models.py - PrintJob model, needs_weighing field on Spool
  • spoolman/database/print_job.py - CRUD operations for print jobs
  • spoolman/api/v1/print_job.py - REST API endpoints
    • POST /print-job - Create pending job
    • POST /print-job/{id}/complete - Complete job, deduct filament
    • POST /print-job/{id}/cancel - Cancel job, flag spool for weighing
    • GET /print-job - List jobs with status filter
  • migrations/versions/2025_01_15_0400-c3d4e5f6g7h8_add_print_jobs.py

Frontend:

  • client/src/pages/print-queue/index.tsx - Print Queue UI page
  • client/src/pages/spools/model.tsx - IPrintJob interface, needs_weighing on ISpool

Slicer Script:

  • scripts/slicer_post_process.py - Elegoo/Orca Slicer post-processing script
  • Parses G-code comments for filament usage (weight in grams)
  • Creates pending print job via API
  • Configure via env vars: SPOOLMAN_URL, SPOOLMAN_SPOOL_ID
  • Or config file: ~/.config/spoolman/slicer.conf

Deployment

Production (Unraid)

Deployed on Unraid with br0 network:

Update script: /mnt/user/appdata/spoolman2-build/update.sh

ssh root@192.168.0.5 "/mnt/user/appdata/spoolman2-build/update.sh"

Known Issues / TODO

Parse Temperature from Comments

Some filaments have temp info in comment field (e.g., "extrude 190-230 / bed 50-70") but not in the min/max fields. Could add a migration or button to parse and populate.

Filament Select Not Refreshing After Inline Creation

When creating a filament via the inline modal on spool create page, the select dropdown doesn't immediately show the new filament with its name (shows ID instead).

3dfilamentprofiles Sample Data

The 3dfp GitHub repo only contains sample data (3 filaments). The full dataset may require using their website API directly.

Future: Issue #9 - Hierarchical Locations

Add room/bin/shelf support for locations. See Gitea issue #9 for details.