Files
battery-tracker-app/CLAUDE.md
T

3.1 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Common Commands

# 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.