import os from sqlalchemy import create_engine, text from sqlalchemy.orm import Session _DB_HOST = os.getenv("DB_HOST", "localhost") _DB_PORT = os.getenv("DB_PORT", "5432") _DB_NAME = os.getenv("DB_NAME", "homestead") _DB_USER = os.getenv("DB_USER", "homestead") _DB_PASSWORD = os.getenv("DB_PASSWORD", "homestead") DATABASE_URL = ( f"postgresql+psycopg2://{_DB_USER}:{_DB_PASSWORD}@{_DB_HOST}:{_DB_PORT}/{_DB_NAME}" ) engine = create_engine(DATABASE_URL, pool_pre_ping=True) def get_session(): with Session(engine) as session: yield session def init_db(): """Create schemas and tables, then seed initial users.""" from app.auth import seed_users with engine.connect() as conn: # Create auth schema and users table conn.execute(text("CREATE SCHEMA IF NOT EXISTS auth")) conn.execute(text(""" CREATE TABLE IF NOT EXISTS auth.users ( id SERIAL PRIMARY KEY, username TEXT NOT NULL UNIQUE, hashed_password TEXT NOT NULL ) """)) # Create lk schema and tables conn.execute(text("CREATE SCHEMA IF NOT EXISTS lk")) conn.execute(text(""" CREATE TABLE IF NOT EXISTS lk.lists ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, type TEXT NOT NULL CHECK (type IN ('shopping', 'todo', 'project')), t_shirt_size TEXT CHECK (t_shirt_size IN ('XS', 'S', 'M', 'L', 'XL')), status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'closed')), notes TEXT, created_at TIMESTAMPTZ DEFAULT now(), closed_at TIMESTAMPTZ ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS lk.list_items ( id SERIAL PRIMARY KEY, list_id INTEGER NOT NULL REFERENCES lk.lists(id) ON DELETE CASCADE, text TEXT NOT NULL, checked BOOLEAN NOT NULL DEFAULT FALSE, position INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ DEFAULT now() ) """)) # Create public schema beekeeper tables conn.execute(text(""" CREATE TABLE IF NOT EXISTS apiaries ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, location_text TEXT, lat REAL, lng REAL, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS colonies ( id SERIAL PRIMARY KEY, apiary_id INTEGER REFERENCES apiaries(id), name TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'wirtschaftsvolk', parent_colony_id INTEGER REFERENCES colonies(id), current_queen_id INTEGER, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, closed_at TIMESTAMPTZ ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS colony_locations ( id SERIAL PRIMARY KEY, colony_id INTEGER NOT NULL REFERENCES colonies(id), apiary_id INTEGER NOT NULL REFERENCES apiaries(id), moved_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS queens ( id SERIAL PRIMARY KEY, colony_id INTEGER REFERENCES colonies(id), year INTEGER, breed TEXT, origin_colony_id INTEGER REFERENCES colonies(id), marked_color TEXT, notes TEXT, introduced_at TEXT, superseded_at TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS inspections ( id SERIAL PRIMARY KEY, colony_id INTEGER NOT NULL REFERENCES colonies(id), date TEXT NOT NULL, inspector TEXT, brood_frames INTEGER, honey_frames INTEGER, food_stores_kg REAL, colony_strength INTEGER, queen_seen INTEGER NOT NULL DEFAULT 0, eggs_seen INTEGER NOT NULL DEFAULT 0, queen_cells_count INTEGER NOT NULL DEFAULT 0, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS inspection_traits ( id SERIAL PRIMARY KEY, inspection_id INTEGER NOT NULL REFERENCES inspections(id) ON DELETE CASCADE, trait TEXT NOT NULL, score INTEGER NOT NULL CHECK (score BETWEEN 1 AND 4) ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS varroa_counts ( id SERIAL PRIMARY KEY, colony_id INTEGER NOT NULL REFERENCES colonies(id), date_from TEXT, date TEXT NOT NULL, time TEXT, method TEXT NOT NULL, count REAL NOT NULL, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS varroa_treatments ( id SERIAL PRIMARY KEY, colony_id INTEGER NOT NULL REFERENCES colonies(id), date TEXT NOT NULL, time TEXT, product TEXT NOT NULL, method TEXT, dosage TEXT, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS varroa_controls ( id SERIAL PRIMARY KEY, treatment_id INTEGER NOT NULL REFERENCES varroa_treatments(id) ON DELETE CASCADE, colony_id INTEGER NOT NULL REFERENCES colonies(id), date TEXT NOT NULL, time TEXT, result TEXT, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS colony_actions ( id SERIAL PRIMARY KEY, colony_id INTEGER NOT NULL REFERENCES colonies(id), date TEXT NOT NULL, time TEXT, type TEXT NOT NULL, notes TEXT, related_colony_id INTEGER REFERENCES colonies(id), created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS honey_harvests ( id SERIAL PRIMARY KEY, colony_id INTEGER REFERENCES colonies(id), apiary_id INTEGER REFERENCES apiaries(id), date TEXT NOT NULL, time TEXT, weight_kg REAL NOT NULL, type TEXT, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS checklists ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, scope TEXT NOT NULL DEFAULT 'colony', created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS checklist_items ( id SERIAL PRIMARY KEY, checklist_id INTEGER NOT NULL REFERENCES checklists(id) ON DELETE CASCADE, text TEXT NOT NULL, position INTEGER NOT NULL DEFAULT 0 ) """)) conn.execute(text(""" CREATE TABLE IF NOT EXISTS checklist_completions ( id SERIAL PRIMARY KEY, checklist_id INTEGER NOT NULL REFERENCES checklists(id), item_id INTEGER NOT NULL REFERENCES checklist_items(id), scope_id INTEGER NOT NULL, date TEXT NOT NULL, completed_by TEXT, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ) """)) # Add columns that may be missing from older installs (idempotent) for stmt in [ "ALTER TABLE varroa_counts ADD COLUMN IF NOT EXISTS date_from TEXT", "ALTER TABLE varroa_counts ADD COLUMN IF NOT EXISTS time TEXT", "ALTER TABLE varroa_treatments ADD COLUMN IF NOT EXISTS time TEXT", "ALTER TABLE varroa_controls ADD COLUMN IF NOT EXISTS time TEXT", "ALTER TABLE honey_harvests ADD COLUMN IF NOT EXISTS time TEXT", "ALTER TABLE colony_actions ADD COLUMN IF NOT EXISTS related_colony_id INTEGER REFERENCES colonies(id)", ]: try: conn.execute(text(stmt)) except Exception: pass conn.commit() seed_users()