Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
19 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
- Edit models in
spoolman/database/models.py - Run the server to update local SQLite
pdm run alembic revision -m "description" --autogenerate- Review generated migration, format with Black/Ruff
Architecture
Backend (/spoolman/)
main.py- FastAPI app entry, startup/shutdown, SPA mountingenv.py- Environment variable parsing and configurationws.py- WebSocket manager for real-time updatesapi/v1/- REST endpoints and WebSocket handlersrouter.py- Main router with WebSocket root endpointspool.py,filament.py,vendor.py- CRUD endpoints per resourcemodels.py- Pydantic response models
database/- SQLAlchemy models and async database operationsmodels.py- ORM definitions (Vendor, Filament, Spool, Setting, *Field)database.py- Connection setup, backup logic, migrations
Frontend (/client/src/)
App.tsx- Routing and Refine framework setuppages/- Page components (spools/, filaments/, vendors/, settings/)components/dataProvider.ts- REST API integrationcomponents/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/orWS /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 CORSVITE_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
- URL: http://192.168.0.5:3022/tonym/spoolman2
- API Token: 8a04b3cb5dbb54e2d895b707305523c3ad83a945
- Upstream: https://github.com/Donkie/Spoolman (remote:
upstream)
This is a fork with UX improvements. Issues are tracked on the Gitea instance above.
UX Improvement Issues (Gitea)
| # | 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 | ❌ REMOVED |
| 8 | (duplicate of #7) | ✅ DONE |
| 9 | Hierarchical Locations (Room/Bin) | ✅ DONE |
| 10 | Database Import/Export | ✅ DONE |
| 11 | Slicer Integration / Print Jobs | ✅ DONE |
| 12 | Redesign Home Page Dashboard | ✅ DONE |
| 13 | Batch Operations (Multi-Select Edit) | ✅ DONE |
| 14 | Extra Weight Field (DryPods, etc.) | ✅ DONE |
| 15 | Spool Count per Filament in List | ✅ DONE |
Other Features (not tracked in Gitea)
- ✅ Theme Colors (5 options)
- ✅ Clickable Dashboard Cards
- ✅ QR Code Size Setting
- ✅ Parse Temps from Comments
- ✅ Prev/Next Navigation on Edit Pages
- ✅ Price/Cost Tracking (remaining value, inventory value)
- ✅ Print History on Spool Detail
- ✅ Usage Analytics Dashboard
- ✅ Color Column in Spool List (with filter)
Issue #1 Complete - Inline Creation Modals
Added ability to create filament/vendor inline without leaving the page:
client/src/components/vendorCreateModal.tsx- Quick vendor creationclient/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
dropdownRenderpattern (same as location select) - Uses
useInvalidateto 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
qrCodePaddingsetting (0-10mm, default 2mm) - Added
qrCodeMaxWidthsetting (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 interfaceclient/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 columnsmigrations/versions/2025_01_15_0200-*.py- Migrationspoolman/api/v1/models.py,filament.py- API changesclient/src/pages/filaments/create.tsx- Form updatesclient/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- AddedSpoolAdjustmentmodel with relationship to Spoolmigrations/versions/2025_01_15_0300-*.py- Migration for spool_adjustment tablespoolman/api/v1/models.py- AddedSpoolAdjustmentPydantic modelspoolman/api/v1/spool.py- Added comment field to use/measure endpoints, GET /spool/{id}/adjustments endpointspoolman/database/spool.py- Updated use_weight, use_length, measure functions to record adjustments
Frontend:
client/src/pages/spools/model.tsx- AddedISpoolAdjustmentinterfaceclient/src/pages/spools/show.tsx- Collapsible adjustment history tableclient/public/locales/en/common.json- Translation keys for history table
Issue #7 REMOVED - 3dfilamentprofiles Integration
Removed - 3dfilamentprofiles.com has Vercel bot protection blocking automated requests. Their GitHub repo only has sample data (~3 filaments). SpoolmanDB has 6,957+ filaments, so this was unnecessary.
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 filePOST /api/v1/backup/import- Imports JSON withreplaceoption- Exports: vendors, filaments, spools, spool_adjustments, settings
- Handles ID remapping to maintain relationships
- Requires
python-multipartpackage 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:
- Export from PostgreSQL instance via Settings → Download Backup
- Reconfigure container for SQLite
- 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- AddedqrCodeSizeto interfaceclient/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:
- Slicer post-processing script parses G-code for filament usage
- Script creates pending print job in Spoolman via API
- After print completes, user visits Print Queue UI
- Complete: Deducts filament from spool, records adjustment history
- Cancel: Flags spool for manual weigh-in (no automatic deduction)
Backend:
spoolman/database/models.py-PrintJobmodel,needs_weighingfield on Spoolspoolman/database/print_job.py- CRUD operations for print jobsspoolman/api/v1/print_job.py- REST API endpointsPOST /print-job- Create pending jobPOST /print-job/{id}/complete- Complete job, deduct filamentPOST /print-job/{id}/cancel- Cancel job, flag spool for weighingGET /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 pageclient/src/pages/spools/model.tsx-IPrintJobinterface,needs_weighingon 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
Color Column in Spool List
Added optional color column to the spool list with filtering capability.
Backend:
spoolman/database/filament.py-find_colors()function to get distinct color hex codesspoolman/api/v1/other.py-GET /api/v1/colorendpoint to list all colorsspoolman/api/v1/spool.py- Addedfilament.color_hexquery parameter for filteringspoolman/database/spool.py- Filter implementation (supports comma-separated values)
Frontend:
client/src/components/column.tsx-ColorFilterColumncomponent renders color swatchesclient/src/components/otherModels.tsx-useSpoolmanColors()hook to fetch colorsclient/src/pages/spools/list.tsx- Added color column (hidden by default)client/public/locales/en/common.json- Added "Color" translation key
Features:
- Color displayed as small swatch with hex tooltip
- Filter dropdown shows swatches with hex codes
- Multi-select filtering (can filter by multiple colors)
- Column hidden by default (enable via column selector)
Issue #9 Complete - Hierarchical Locations
Storage locations can now be organized in a tree structure (Room > Shelf > Bin).
Backend:
spoolman/database/models.py- AddedLocationmodel with self-referential parentspoolman/database/location.py- CRUD operations, tree building, full path resolutionspoolman/api/v1/location.py- REST API for locationsmigrations/versions/2025_01_21_0100-*.py- Migration for location table
Frontend:
client/src/components/LocationTreeSelect.tsx- Tree-based location pickerclient/src/components/LocationCreateModal.tsx- Inline location creation
Features:
- Hierarchical structure with parent/child relationships
- Location types: room, shelf, bin, drawer, box, other
- Legacy flat location string preserved for backward compatibility
- API endpoints: GET /location, GET /location/tree, POST/PATCH/DELETE
Issue #12 Complete - Dashboard Redesign
Redesigned home page with better visualizations and quick stats.
Backend: No changes needed - uses existing endpoints.
Frontend:
client/src/pages/home/index.tsx- Refactored layoutclient/src/pages/home/components/AlertCards.tsx- Low stock, pending jobs, needs weighing alertsclient/src/pages/home/components/MaterialDistributionChart.tsx- Pie chart by material (recharts)client/src/pages/home/components/QuickStats.tsx- Total spools, filaments, vendors, weight
Dependencies:
- Added
rechartspackage for charts
Issue #13 Complete - Batch Operations
Multi-select spools with bulk archive/delete/move actions.
Backend:
spoolman/api/v1/spool.py- Added bulk endpoints:- POST /spool/bulk/archive
- POST /spool/bulk/delete
- POST /spool/bulk/update
Frontend:
client/src/pages/spools/list.tsx- Added row selectionclient/src/pages/spools/components/BatchActionBar.tsx- Floating action barclient/src/pages/spools/functions.tsx- Bulk helper functions
Features:
- Checkbox selection with select-all
- Floating action bar shows when items selected
- Archive, unarchive, delete, move to location
- Confirmation modals for all destructive actions
Issue #14 Complete - Extra Weight Field
Track extra weight from DryPods, custom spool holders, or other accessories.
Backend:
spoolman/database/models.py- Addedextra_weightcolumn to Spool modelspoolman/database/spool.py- Updatedmeasure()to account for extra weightspoolman/api/v1/spool.py- Addedextra_weightto create/update parametersmigrations/versions/2025_01_21_0200-*.py- Migration for extra_weight column
Frontend:
client/src/pages/spools/create.tsx- Added extra_weight form fieldclient/src/pages/spools/edit.tsx- Added extra_weight form field- Weight calculations updated to include extra_weight in gross weight
Features:
- Optional field for tracking additional weight on spool
- Automatically subtracted when measuring spool weight
- Useful for DryPods, custom spool holders, reel adapters
Issue #15 Complete - Spool Count per Filament
Already implemented - filament list shows spool_count and total_remaining_weight columns.
Price & Cost Tracking
Track spool purchase price and calculate remaining value.
Backend:
spoolman/api/v1/models.py- Addedremaining_valuecomputed field- Formula:
remaining_value = (remaining_weight / initial_weight) * price - Price falls back to filament price if not set on spool
Frontend:
client/src/pages/spools/list.tsx- Added remaining_value column (hidden by default)client/src/pages/home/components/QuickStats.tsx- Added "Inventory Value" stat
Print History on Spool Detail
Show print job history on the spool detail page.
Frontend:
client/src/pages/spools/show.tsx- Added collapsible print history table- Fetches print jobs filtered by spool_id
- Shows: Created date, Filename, Filament Used, Status, Finished date
Usage Analytics Dashboard
Time-series charts showing filament consumption trends.
Backend:
spoolman/api/v1/other.py- New analytics endpoints:GET /analytics/usage?days=N- Daily usage from SpoolAdjustmentsGET /analytics/by-material?days=N- Usage breakdown by material
Frontend:
client/src/pages/home/components/UsageAnalytics.tsx- New component- Area chart showing daily consumption (recharts)
- Period selector: 7, 30, or 90 days
- Material breakdown cards showing usage by filament type
Deployment
Production (Unraid)
Deployed on Unraid with br0 network:
- URL: http://192.168.0.11:8000 or https://spoolman.nastynas.xyz
- Database: SQLite
- Network: br0 (own IP: 192.168.0.11)
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).
Issue #7 - 3dfilamentprofiles Integration REMOVED
The 3dfilamentprofiles.com website has Vercel bot protection that blocks automated requests. Their GitHub repo (jklewa/filament-profiles-data) only provides sample data (~3 filaments). Since SpoolmanDB already has 6,957+ filaments from major brands, the 3dfp integration was removed rather than maintaining dead code.