Files
homekeeper/api/app/database.py
T
2026-07-06 19:25:38 +02:00

244 lines
10 KiB
Python

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()