--- phase: 01-foundation plan: 02 type: execute wave: 2 depends_on: ["01-01"] files_modified: - imptune/db/__init__.py - imptune/db/database.py - imptune/db/models.py - imptune/storage/__init__.py - imptune/storage/driver_store.py - imptune/main.py - tests/test_db.py autonomous: true requirements: - INFRA-01 - INFRA-02 must_haves: truths: - "SQLite database initializes automatically on first run with all tables (Client, Driver, Printer, Icon)" - "Database uses WAL journal mode and has foreign keys enabled" - "Database file is created inside the DATA_DIR volume path, not inside the container filesystem" - "Schema creation is idempotent — repeated startups do not fail or duplicate tables" artifacts: - path: "imptune/db/database.py" provides: "Peewee SqliteDatabase instance with WAL mode and init_db function" exports: ["db", "init_db"] - path: "imptune/db/models.py" provides: "All ORM models for phases 1-5 (BaseModel, Client, Driver, Printer, Icon)" exports: ["BaseModel", "Client", "Driver", "Printer", "Icon"] - path: "imptune/storage/driver_store.py" provides: "SHA256 content-addressed file storage abstraction for driver packages" exports: ["DriverStore"] - path: "tests/test_db.py" provides: "Database initialization and schema validation tests" key_links: - from: "imptune/main.py" to: "imptune/db/database.py" via: "startup event calling init_db()" pattern: "init_db" - from: "imptune/db/models.py" to: "imptune/db/database.py" via: "BaseModel.Meta.database = db" pattern: "database = db" - from: "imptune/db/database.py" to: "imptune/config.py" via: "DB_PATH from config" pattern: "DB_PATH|DATA_DIR" --- Create the full SQLite schema using Peewee ORM (all tables for phases 1-5) and the content-addressed driver storage abstraction. Wire database initialization into the FastAPI startup event. Purpose: Establish the data layer that all subsequent phases depend on. The full schema is created upfront per the locked user decision, so later phases only add routes and logic — not schema changes. Satisfies INFRA-01 (SQLite auto-init) and INFRA-02 (no external DB). Output: Working database module with all models, driver storage helper, and startup wiring. @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-foundation/01-CONTEXT.md @.planning/phases/01-foundation/01-RESEARCH.md From imptune/config.py: ```python DATA_DIR: str # env var, default "/data" DB_PATH: str # DATA_DIR + "/imptune.db" DRIVERS_DIR: str # DATA_DIR + "/drivers" ``` From imptune/main.py: ```python app = FastAPI(title="ImpTune") # startup event already creates DATA_DIR/DRIVERS_DIR directories # Executor must ADD init_db() call to the existing startup event ``` Task 1: Create Peewee models, database init, and driver storage imptune/db/__init__.py, imptune/db/database.py, imptune/db/models.py, imptune/storage/__init__.py, imptune/storage/driver_store.py - test_create_tables: calling init_db() creates all 4 tables (client, driver, printer, icon) in a fresh SQLite file - test_wal_mode: after init_db(), PRAGMA journal_mode returns "wal" - test_foreign_keys: after init_db(), PRAGMA foreign_keys returns 1 - test_idempotent: calling init_db() twice does not raise an error - test_driver_store_save: saving bytes returns their SHA256 hex digest and creates a file at DRIVERS_DIR/{sha256} - test_driver_store_dedup: saving the same bytes twice results in one file on disk (not two) - test_driver_store_get_path: get_path(sha256) returns the correct file path **imptune/db/database.py**: - Import SqliteDatabase from peewee, import DB_PATH from imptune.config - Create db = SqliteDatabase(None) (deferred init — path set at runtime so tests can override) - init_db() function: call db.init(DB_PATH, pragmas={"journal_mode": "wal", "foreign_keys": 1}), then db.connect(reuse_if_open=True), then import all models and call db.create_tables([Client, Driver, Printer, Icon], safe=True) - Use deferred database pattern so tests can point at a temp file **imptune/db/models.py** (full schema for all phases per locked decision): - BaseModel with Meta.database = db - Client: name (CharField unique), created_at (DateTimeField default utcnow) - Driver: sha256 (CharField unique, indexed), original_filename (CharField), size_bytes (IntegerField), uploaded_at (DateTimeField default utcnow), driver_desc (CharField null=True), inf_filename (CharField null=True), architecture (CharField null=True), has_cat_file (BooleanField default=False) - Printer: name (CharField), ip_address (CharField), port_name (CharField), client (ForeignKeyField Client null=True backref="printers"), driver (ForeignKeyField Driver null=True backref="printers"), duplex_mode (CharField default="OneSided"), color_mode (BooleanField default=True), paper_size (CharField default="A4"), collate (BooleanField default=True), created_at, updated_at (both DateTimeField default utcnow) - Icon: printer (ForeignKeyField Printer unique backref="icons"), sha256 (CharField), original_filename (CharField), size_bytes (IntegerField), uploaded_at (DateTimeField default utcnow) **imptune/storage/driver_store.py**: - Class DriverStore with __init__(self, base_dir: str) - save(self, data: bytes) -> str: compute SHA256, write to base_dir/{sha256} if not exists, return hex digest - get_path(self, sha256: str) -> Path: return Path(base_dir) / sha256 - exists(self, sha256: str) -> bool: check if file exists cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && python -m pytest tests/test_db.py -x -v - All 7 tests pass - init_db() creates Client, Driver, Printer, Icon tables - WAL mode and foreign keys enabled - Idempotent — second call is no-op - DriverStore deduplicates by SHA256 Task 2: Wire database init into FastAPI startup imptune/main.py Modify the existing imptune/main.py (created by plan 01-01) to add database initialization on startup: - Import init_db from imptune.db.database - In the existing startup event handler, add a call to init_db() AFTER the directory creation logic - This ensures the SQLite database is created inside DATA_DIR (which was just created/verified) - Keep all existing code (StaticFiles mount, router includes, directory creation) — only ADD the init_db() call Do NOT use async def for the startup handler — Peewee is sync-only. Use regular def with FastAPI's @app.on_event("startup") which already exists from plan 01-01. cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && python -m pytest tests/test_health.py tests/test_db.py -x -v - main.py imports and calls init_db() on startup - Existing health and static tests still pass (no regression) - Database tests pass with init triggered via app startup - `python -m pytest tests/ -x -v` — all tests pass (health + static + db) - `python -c "from imptune.db.models import Client, Driver, Printer, Icon; print('Models OK')"` — imports without error - `python -c "from imptune.storage.driver_store import DriverStore; print('DriverStore OK')"` — imports without error - SQLite database auto-creates on app startup with 4 tables - WAL journal mode and foreign keys enabled via pragmas - Database file lives at DATA_DIR/imptune.db (volume-mounted path) - DriverStore saves files by SHA256 with deduplication - All existing tests continue to pass (no regression) - 7+ new tests pass for db and storage After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`