Add CLAUDE.md with architecture and dev commands
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Common Commands
|
||||
|
||||
```bash
|
||||
# Activate the venv (required before all commands below)
|
||||
source .venv/bin/activate
|
||||
|
||||
# Run all acceptance tests
|
||||
pytest tests/ -v
|
||||
|
||||
# Run a single test
|
||||
pytest tests/test_acceptance.py::test_assign_battery -v
|
||||
|
||||
# Seed the database (safe to skip if batteries.db already exists)
|
||||
python seed.py
|
||||
|
||||
# Start the dev server
|
||||
flask run
|
||||
|
||||
# Start production server (same as systemd service uses)
|
||||
waitress-serve --host=127.0.0.1 --port=5000 app:app
|
||||
|
||||
# Install/reinstall systemd user service
|
||||
bash sbin/install-service.sh
|
||||
|
||||
# Service management
|
||||
systemctl --user start|stop|restart|status battery-tracker
|
||||
journalctl --user -u battery-tracker -f
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Request lifecycle
|
||||
`app.py` contains `create_app(config_object)` which builds the Flask app, creates the SQLAlchemy engine from `config.SQLALCHEMY_DATABASE_URI`, calls `Base.metadata.create_all(engine)`, and wires a `scoped_session` as the `db` local. All routes close over this `db` via `@app.teardown_appcontext`. There is no Flask-SQLAlchemy — the ORM session is raw SQLAlchemy so that `models.py` can be imported by `migrate_to_mariadb.py` without an app context.
|
||||
|
||||
### Database config
|
||||
`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.
|
||||
|
||||
### 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
|
||||
|
||||
### 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).
|
||||
|
||||
### MariaDB migration
|
||||
`migrate_to_mariadb.py` opens two SQLAlchemy sessions simultaneously (SQLite source, MariaDB destination), migrates Devices first (FK dependency), then Batteries with explicit `id=` values to preserve FK links, then resets `AUTO_INCREMENT` via `text("ALTER TABLE ...")`. Takes `MARIADB_URL` from env or CLI arg. See `MIGRATION.md` for the full procedure.
|
||||
Reference in New Issue
Block a user