Files
spoolman2/CLAUDE.md
tonym 5f66302e73
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
docs: Update CLAUDE.md with full Gitea issue list (#1-#47)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 21:04:17 -06:00

20 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 Open
20 Gallery View for Spools (Color Grid) Open
21 QR Code Version/Complexity Config Open
23 Reporting & Data Export Open
24 NFC/RFID Tag Support Open
25 Sort Spools by Custom Fields Open
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 Open
36 Spool-Level Overrides (Color, Price) Open
37 Plugin System for Extensions Open
39 Usage Statistics & Cost Tracking Partial
40 Storage vs Active Spool Status Open
41 Load Cell / Scale Integration Open
42 Allow Spools Heavier Than Max Open
43 Show Filament Fields on Spool View Open
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"

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.