Files
spoolman2/CLAUDE.md
tonym a1572694e3
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 Issue #10 and Docker deployment notes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 22:50:06 -06:00

330 lines
12 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 |
### 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
### Dev Mode Styling
Files modified for dev version visual distinction:
- `client/src/contexts/color-mode/index.tsx` - Teal primary color (#0891b2)
- `client/src/components/layout.tsx` - "DEV BUILD" banner and badge
Set `IS_DEV = false` in layout.tsx to disable dev styling.
---
## Known Issues / TODO
### 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).
Attempted fixes:
- Added `refetch` from `useGetFilamentSelectOptions` hook
- Called `await refetchFilaments()` before setting form value
Still not working reliably. May need to investigate react-query cache invalidation timing or add the new option manually to the select options array.
### 3dfilamentprofiles Sample Data
The 3dfp GitHub repo only contains sample data (3 filaments). The full dataset may require:
- Using their website API directly (may need auth)
- Waiting for them to publish full JSON exports
- Current implementation uses `sample-filaments.json` from the repo
### Next Up: Docker Deployment to Unraid
Build and deploy this fork as a Docker container on Unraid with SQLite.
Steps:
1. Build Docker image from this repo
2. Push to registry (Docker Hub or Gitea)
3. Deploy on Unraid with br0 network (own IP)
4. Use SQLite (no external DB dependency)
5. Import data via Settings → Import
### Future: Issue #9 - Hierarchical Locations
Add room/bin/shelf support for locations. See Gitea issue #9 for details.