Add README and update CLAUDE.md with current model and rules

This commit is contained in:
2026-04-12 22:58:58 -05:00
parent 768f83f63a
commit 86fb342b0d
2 changed files with 155 additions and 3 deletions
+16 -3
View File
@@ -40,15 +40,28 @@ journalctl --user -u battery-tracker -f
`config.py` reads `DATABASE_URL` from the environment, falling back to `sqlite:///batteries.db`. Swapping to MariaDB is entirely a matter of setting that env var — no code changes needed. All column types are restricted to `Integer`, `String`, `Text`, `Boolean`, `DateTime` for MariaDB compatibility. Status is stored as `String(20)` (not `Enum`) to avoid DDL differences between SQLite and MariaDB.
### Models
- `Battery`: label (unique), brand, status (`available`/`installed`/`retired`), device_id (FK nullable, `ondelete=SET NULL`), notes
- `Device`: name (unique), battery_slots, notes
- Helper methods on both models (`is_available()`, `installed_count()`, `has_mixed_brands()`) are used directly in templates and route logic.
- `Battery`: label (unique), brand, status (`available`/`installed`/`retired`), device_id (FK nullable, `ondelete=SET NULL`), notes, size, chemistry, capacity_mah, tested_capacity_mah, tested_date, charge_cycles, purchase_date, storage_location
- `Device`: name (unique), battery_slots, device_type, notes
- Helper methods: `Battery.is_available/installed/retired()`, `Device.installed_count()`, `Device.installed_brands()`, `Device.has_mixed_brands()`
### Business rules (enforced in routes, not DB constraints)
- Assigning a retired battery → hard block with flash error
- Assigning to a full device → hard block with flash error
- Mixed brands on same device → flash warning, assignment still proceeds
- Deleting a battery → requires GET confirmation page first, then POST
- Bulk install into device: capacity check before write; batteries already in target device are skipped; retired batteries are skipped
- Unretire: sets status back to `available`
- Unassign with `next` form field → redirects to that URL (must start with `/`); falls back to dashboard
### Adding new columns to existing DB
`create_all()` won't add columns to existing tables. Run via Python:
```python
import sqlite3
conn = sqlite3.connect('batteries.db')
conn.execute('ALTER TABLE <table> ADD COLUMN <col> <type>')
conn.commit(); conn.close()
```
Always snapshot the DB first: `cp batteries.db batteries.db.$(date +%Y-%m-%d).snapshot`
### Testing
Tests use a temporary file-based SQLite DB (via `tempfile.mkstemp`) created fresh per test — avoids SQLite in-memory per-connection isolation issues. The `seeded_client` fixture in `conftest.py` pre-populates via HTTP POST calls (not direct DB access), so tests exercise the full stack. Battery IDs in tests are positional (id=1 is always the first battery POSTed by the fixture).