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

381 lines
14 KiB
Markdown

# 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)
```bash
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
```bash
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
```bash
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:
```bash
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:
```bash
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
- **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
| # | 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_name``manufacturer`
- `color` + `material_type``name`
- `rgb``color_hex` (strip # prefix)
- `properties.temp_min/max``extruder_temp/extruder_temp_max`
- `properties.bed_temp_min/max``bed_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:
```bash
# 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:
- **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`
```bash
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.