Files
spoolman2/CLAUDE.md
tonym beb1ad2bfa
Some checks failed
CI / style (push) Has been cancelled
CI / build-client (push) Has been cancelled
CI / build-tester (push) Has been cancelled
CI / build-amd64 (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
Issue Manager / issue-manager (push) Failing after 9s
docs: Update CLAUDE.md with completed issue #19
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 00:40:18 -06:00

24 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 (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
16 Filter Spools by Color DONE
17 Image Attachments for Filaments Open
18 QR Code Labels for Filaments Open
19 Integration with filamentcolors.xyz DONE
20 Gallery View for Spools (Color Grid) DONE
21 QR Code Version/Complexity Config Open
23 Reporting & Data Export Open
24 NFC/RFID Tag Support Open
25 Sort Spools by Custom Fields DONE
26 Label Template Enhancements Open
27 QR Scanner Camera Device Selector Open
28 Mobile-Friendly UI / Responsive Open
29 Table Column Customization Open
30 Printer Management & Parts Inventory Open
31 Special Filament Properties Open
32 SpoolType Model (Cardboard/Plastic/Metal) Open
34 Color Swatch Border for Dark Mode DONE
35 Global Search Box DONE
36 Spool-Level Overrides (Color, Price) DONE
37 Plugin System for Extensions Open
39 Usage Statistics & Cost Tracking DONE
40 Storage vs Active Spool Status Open
41 Load Cell / Scale Integration Open
42 Allow Spools Heavier Than Max DONE
43 Show Filament Fields on Spool View DONE
44 HomeAssistant Add-On Packaging Open
45 Filament Swatch Card Generator Open
46 Project-Based Spool Grouping Open
47 G-code Filament Usage Estimation Open

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 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 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 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

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 codes
  • spoolman/api/v1/other.py - GET /api/v1/color endpoint to list all colors
  • spoolman/api/v1/spool.py - Added filament.color_hex query parameter for filtering
  • spoolman/database/spool.py - Filter implementation (supports comma-separated values)

Frontend:

  • client/src/components/column.tsx - ColorFilterColumn component renders color swatches
  • client/src/components/otherModels.tsx - useSpoolmanColors() hook to fetch colors
  • client/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 - Added Location model with self-referential parent
  • spoolman/database/location.py - CRUD operations, tree building, full path resolution
  • spoolman/api/v1/location.py - REST API for locations
  • migrations/versions/2025_01_21_0100-*.py - Migration for location table

Frontend:

  • client/src/components/LocationTreeSelect.tsx - Tree-based location picker
  • client/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 layout
  • client/src/pages/home/components/AlertCards.tsx - Low stock, pending jobs, needs weighing alerts
  • client/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 recharts package 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 selection
  • client/src/pages/spools/components/BatchActionBar.tsx - Floating action bar
  • client/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 - Added extra_weight column to Spool model
  • spoolman/database/spool.py - Updated measure() to account for extra weight
  • spoolman/api/v1/spool.py - Added extra_weight to create/update parameters
  • migrations/versions/2025_01_21_0200-*.py - Migration for extra_weight column

Frontend:

  • client/src/pages/spools/create.tsx - Added extra_weight form field
  • client/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 - Added remaining_value computed 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 SpoolAdjustments
    • GET /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:

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

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

Issue #42 Complete - Allow Spools Heavier Than Max

Removed weight validation cap that prevented spools from exceeding their theoretical initial weight.

Backend:

  • spoolman/database/spool.py - Removed max(..., 0) clamps from create/update; when remaining > initial, adjusts initial upward
  • spoolman/api/v1/models.py - Removed max(..., 0) from remaining_weight computation

Issue #43 Complete - Show Filament Fields on Spool View

Display filament custom fields on the spool detail page.

Frontend:

  • client/src/pages/spools/show.tsx - Added filamentExtraFields section after spool extra fields

Issue #25 Complete - Sort by Custom Fields & Remaining Weight

Backend:

  • spoolman/database/spool.py - Added extra.{key} sort support via outerjoin on SpoolField table

Frontend:

  • client/src/pages/spools/list.tsx - Added sorter: true to weight/length NumberColumns
  • client/src/components/column.tsx - Changed CustomFieldColumn to sortable with dataId: extra.{key}

Backend:

  • spoolman/api/v1/spool.py - Added q parameter to find endpoint
  • spoolman/database/spool.py - OR-based search across filament name, material, vendor name, location, lot_nr, comment

Frontend:

  • client/src/pages/spools/list.tsx - Added Input.Search component, passes q via meta.queryParams

Color grid view for spools with progress bars and material labels.

Frontend:

  • client/src/pages/spools/components/SpoolGallery.tsx - Grid of color cards with progress bars
  • client/src/pages/spools/list.tsx - View mode toggle (table/gallery)

Issue #36 Complete - Spool-Level Overrides

Per-spool color override with ColorPicker (price and initial_weight already existed).

Backend:

  • spoolman/database/models.py - Added color_hex field to Spool model
  • spoolman/api/v1/spool.py - Added color_hex to create/update params
  • migrations/versions/2025_01_22_0100-g7h8i9j0k1l2_add_spool_color_hex.py

Frontend:

  • client/src/pages/spools/create.tsx, edit.tsx - ColorPicker form field
  • All display locations updated to prefer spool.color_hex over filament.color_hex

Issue #39 Complete - Usage Statistics & Cost Tracking

Added cost analytics endpoint and total spent / avg cost per kg stats.

Backend:

  • spoolman/api/v1/other.py - Added GET /analytics/cost endpoint returning total_spent, avg_cost_per_kg, cost_by_material

Frontend:

  • client/src/pages/home/components/QuickStats.tsx - Added "Total Spent" and "Avg Cost/kg" stat cards
  • client/src/pages/home/components/UsageAnalytics.tsx - Added cost per material in breakdown cards

Issue #19 Complete - filamentcolors.xyz Integration

Color lookup from filamentcolors.xyz database (2134 swatches, 317 manufacturers).

Backend:

  • spoolman/api/v1/other.py - Proxy endpoints:
    • GET /filamentcolors/manufacturers - List all manufacturers
    • GET /filamentcolors/search - Search swatches by manufacturer, color, filament type

Frontend:

  • client/src/components/filamentColorLookup.tsx - Modal with manufacturer filter + color grid
  • Added search button next to ColorPicker on filament create and edit forms
  • Click a swatch to apply its measured hex color

Bug Fixes

  • Filament Select Refresh: Fixed race condition where form value was set before options re-rendered. Uses pendingFilamentId state + useEffect.
  • Parse Temps from Comments: Added regex-based temperature parser button on filament edit page.

Known Issues / TODO

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.