277 lines
6.6 KiB
Go
277 lines
6.6 KiB
Go
package database
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"mealprep/models"
|
|
"time"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
var DB *sql.DB
|
|
|
|
// InitDB initializes the database and creates tables
|
|
func InitDB(dbPath string) error {
|
|
var err error
|
|
DB, err = sql.Open("sqlite3", dbPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open database: %w", err)
|
|
}
|
|
|
|
// Test connection
|
|
if err = DB.Ping(); err != nil {
|
|
return fmt.Errorf("failed to ping database: %w", err)
|
|
}
|
|
|
|
// Create tables
|
|
if err = createTables(); err != nil {
|
|
return fmt.Errorf("failed to create tables: %w", err)
|
|
}
|
|
|
|
// Run migrations
|
|
if err = runMigrations(); err != nil {
|
|
return fmt.Errorf("failed to run migrations: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createTables() error {
|
|
schema := `
|
|
CREATE TABLE IF NOT EXISTS ingredients (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
unit TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS meals (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
meal_type TEXT NOT NULL DEFAULT 'lunch'
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS meal_ingredients (
|
|
meal_id INTEGER NOT NULL,
|
|
ingredient_id INTEGER NOT NULL,
|
|
quantity REAL NOT NULL,
|
|
PRIMARY KEY (meal_id, ingredient_id),
|
|
FOREIGN KEY (meal_id) REFERENCES meals(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (ingredient_id) REFERENCES ingredients(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS week_plan (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
date TEXT NOT NULL,
|
|
meal_id INTEGER NOT NULL,
|
|
FOREIGN KEY (meal_id) REFERENCES meals(id) ON DELETE CASCADE
|
|
);
|
|
`
|
|
|
|
_, err := DB.Exec(schema)
|
|
return err
|
|
}
|
|
|
|
func runMigrations() error {
|
|
// Check if meal_type column exists
|
|
var count int
|
|
err := DB.QueryRow("SELECT COUNT(*) FROM pragma_table_info('meals') WHERE name='meal_type'").Scan(&count)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Add meal_type column if it doesn't exist
|
|
if count == 0 {
|
|
_, err = DB.Exec("ALTER TABLE meals ADD COLUMN meal_type TEXT NOT NULL DEFAULT 'lunch'")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Ingredient operations
|
|
|
|
func GetAllIngredients() ([]models.Ingredient, error) {
|
|
rows, err := DB.Query("SELECT id, name, unit FROM ingredients ORDER BY name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var ingredients []models.Ingredient
|
|
for rows.Next() {
|
|
var ing models.Ingredient
|
|
if err := rows.Scan(&ing.ID, &ing.Name, &ing.Unit); err != nil {
|
|
return nil, err
|
|
}
|
|
ingredients = append(ingredients, ing)
|
|
}
|
|
return ingredients, nil
|
|
}
|
|
|
|
func AddIngredient(name, unit string) (int64, error) {
|
|
result, err := DB.Exec("INSERT INTO ingredients (name, unit) VALUES (?, ?)", name, unit)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.LastInsertId()
|
|
}
|
|
|
|
func DeleteIngredient(id int) error {
|
|
_, err := DB.Exec("DELETE FROM ingredients WHERE id = ?", id)
|
|
return err
|
|
}
|
|
|
|
// Meal operations
|
|
|
|
func GetAllMeals() ([]models.Meal, error) {
|
|
rows, err := DB.Query("SELECT id, name, description, meal_type FROM meals ORDER BY name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var meals []models.Meal
|
|
for rows.Next() {
|
|
var meal models.Meal
|
|
if err := rows.Scan(&meal.ID, &meal.Name, &meal.Description, &meal.MealType); err != nil {
|
|
return nil, err
|
|
}
|
|
meals = append(meals, meal)
|
|
}
|
|
return meals, nil
|
|
}
|
|
|
|
func GetMealByID(id int) (*models.Meal, error) {
|
|
var meal models.Meal
|
|
err := DB.QueryRow("SELECT id, name, description, meal_type FROM meals WHERE id = ?", id).
|
|
Scan(&meal.ID, &meal.Name, &meal.Description, &meal.MealType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &meal, nil
|
|
}
|
|
|
|
func AddMeal(name, description, mealType string) (int64, error) {
|
|
result, err := DB.Exec("INSERT INTO meals (name, description, meal_type) VALUES (?, ?, ?)", name, description, mealType)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.LastInsertId()
|
|
}
|
|
|
|
func DeleteMeal(id int) error {
|
|
_, err := DB.Exec("DELETE FROM meals WHERE id = ?", id)
|
|
return err
|
|
}
|
|
|
|
// Meal Ingredients operations
|
|
|
|
func GetMealIngredients(mealID int) ([]models.MealIngredient, error) {
|
|
query := `
|
|
SELECT mi.meal_id, mi.ingredient_id, mi.quantity, i.name, i.unit
|
|
FROM meal_ingredients mi
|
|
JOIN ingredients i ON mi.ingredient_id = i.id
|
|
WHERE mi.meal_id = ?
|
|
ORDER BY i.name
|
|
`
|
|
rows, err := DB.Query(query, mealID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var ingredients []models.MealIngredient
|
|
for rows.Next() {
|
|
var mi models.MealIngredient
|
|
if err := rows.Scan(&mi.MealID, &mi.IngredientID, &mi.Quantity, &mi.IngredientName, &mi.Unit); err != nil {
|
|
return nil, err
|
|
}
|
|
ingredients = append(ingredients, mi)
|
|
}
|
|
return ingredients, nil
|
|
}
|
|
|
|
func AddMealIngredient(mealID, ingredientID int, quantity float64) error {
|
|
_, err := DB.Exec(
|
|
"INSERT OR REPLACE INTO meal_ingredients (meal_id, ingredient_id, quantity) VALUES (?, ?, ?)",
|
|
mealID, ingredientID, quantity,
|
|
)
|
|
return err
|
|
}
|
|
|
|
func DeleteMealIngredient(mealID, ingredientID int) error {
|
|
_, err := DB.Exec("DELETE FROM meal_ingredients WHERE meal_id = ? AND ingredient_id = ?", mealID, ingredientID)
|
|
return err
|
|
}
|
|
|
|
// Week Plan operations
|
|
|
|
func GetWeekPlan() ([]models.WeekPlanEntry, error) {
|
|
query := `
|
|
SELECT wp.id, wp.date, wp.meal_id, m.name, m.meal_type
|
|
FROM week_plan wp
|
|
JOIN meals m ON wp.meal_id = m.id
|
|
ORDER BY wp.date, m.meal_type
|
|
`
|
|
rows, err := DB.Query(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var entries []models.WeekPlanEntry
|
|
for rows.Next() {
|
|
var entry models.WeekPlanEntry
|
|
var dateStr string
|
|
if err := rows.Scan(&entry.ID, &dateStr, &entry.MealID, &entry.MealName, &entry.MealType); err != nil {
|
|
return nil, err
|
|
}
|
|
entry.Date, _ = time.Parse("2006-01-02", dateStr)
|
|
entries = append(entries, entry)
|
|
}
|
|
return entries, nil
|
|
}
|
|
|
|
func AddWeekPlanEntry(date time.Time, mealID int) error {
|
|
dateStr := date.Format("2006-01-02")
|
|
_, err := DB.Exec("INSERT INTO week_plan (date, meal_id) VALUES (?, ?)", dateStr, mealID)
|
|
return err
|
|
}
|
|
|
|
func DeleteWeekPlanEntry(id int) error {
|
|
_, err := DB.Exec("DELETE FROM week_plan WHERE id = ?", id)
|
|
return err
|
|
}
|
|
|
|
// Grocery List operations
|
|
|
|
func GetGroceryList() ([]models.GroceryItem, error) {
|
|
query := `
|
|
SELECT i.name, SUM(mi.quantity) as total_quantity, i.unit
|
|
FROM week_plan wp
|
|
JOIN meal_ingredients mi ON wp.meal_id = mi.meal_id
|
|
JOIN ingredients i ON mi.ingredient_id = i.id
|
|
GROUP BY i.id, i.name, i.unit
|
|
ORDER BY i.name
|
|
`
|
|
rows, err := DB.Query(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var items []models.GroceryItem
|
|
for rows.Next() {
|
|
var item models.GroceryItem
|
|
if err := rows.Scan(&item.IngredientName, &item.TotalQuantity, &item.Unit); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
return items, nil
|
|
}
|