release nouri 0.5.0 shopping rhythm pwa and reminders
This commit is contained in:
+117
-4
@@ -8,7 +8,7 @@ from flask import Flask, current_app, g
|
||||
from flask.cli import with_appcontext
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from .constants import DAYPARTS, DEFAULT_CATEGORIES
|
||||
from .constants import DAYPARTS, DEFAULT_CATEGORIES, DEFAULT_CATEGORY_BUILDERS
|
||||
|
||||
|
||||
def get_db() -> sqlite3.Connection:
|
||||
@@ -53,6 +53,9 @@ def bootstrap_legacy_schema(database: sqlite3.Connection) -> None:
|
||||
CREATE TABLE IF NOT EXISTS households (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
shopping_weekday INTEGER NOT NULL DEFAULT 5,
|
||||
shopping_prep_days INTEGER NOT NULL DEFAULT 1,
|
||||
shopping_reminder_time TEXT NOT NULL DEFAULT '18:00',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"""
|
||||
@@ -64,6 +67,7 @@ def bootstrap_legacy_schema(database: sqlite3.Connection) -> None:
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
household_id INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
builder_key TEXT NOT NULL DEFAULT 'neutral',
|
||||
sort_order INTEGER NOT NULL DEFAULT 100,
|
||||
is_active INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
@@ -72,6 +76,14 @@ def bootstrap_legacy_schema(database: sqlite3.Connection) -> None:
|
||||
"""
|
||||
)
|
||||
|
||||
if table_exists(database, "households"):
|
||||
add_column_if_missing(database, "households", "shopping_weekday INTEGER NOT NULL DEFAULT 5")
|
||||
add_column_if_missing(database, "households", "shopping_prep_days INTEGER NOT NULL DEFAULT 1")
|
||||
add_column_if_missing(database, "households", "shopping_reminder_time TEXT NOT NULL DEFAULT '18:00'")
|
||||
|
||||
if table_exists(database, "household_categories"):
|
||||
add_column_if_missing(database, "household_categories", "builder_key TEXT NOT NULL DEFAULT 'neutral'")
|
||||
|
||||
if table_exists(database, "users"):
|
||||
add_column_if_missing(database, "users", "household_id INTEGER")
|
||||
add_column_if_missing(database, "users", "email TEXT")
|
||||
@@ -92,6 +104,82 @@ def bootstrap_legacy_schema(database: sqlite3.Connection) -> None:
|
||||
add_column_if_missing(database, "shopping_entries", "needed_for_date TEXT")
|
||||
add_column_if_missing(database, "shopping_entries", "needed_for_daypart_id INTEGER")
|
||||
|
||||
if table_exists(database, "shopping_needs"):
|
||||
add_column_if_missing(database, "shopping_needs", "source_item_id INTEGER")
|
||||
add_column_if_missing(database, "shopping_needs", "activation_date TEXT")
|
||||
add_column_if_missing(database, "shopping_needs", "is_activated INTEGER NOT NULL DEFAULT 0")
|
||||
add_column_if_missing(database, "shopping_needs", "activated_at TEXT")
|
||||
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS user_settings (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
reminders_enabled INTEGER NOT NULL DEFAULT 1,
|
||||
push_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
notification_channel TEXT NOT NULL DEFAULT 'in_app',
|
||||
remind_before_shopping INTEGER NOT NULL DEFAULT 1,
|
||||
remind_on_shopping_day INTEGER NOT NULL DEFAULT 1,
|
||||
show_missing_for_upcoming_week INTEGER NOT NULL DEFAULT 1,
|
||||
show_planned_not_shopped INTEGER NOT NULL DEFAULT 1,
|
||||
remind_tomorrow_if_sparse INTEGER NOT NULL DEFAULT 1,
|
||||
remind_week_if_sparse INTEGER NOT NULL DEFAULT 1,
|
||||
suggest_home_for_today INTEGER NOT NULL DEFAULT 1,
|
||||
remind_small_snack INTEGER NOT NULL DEFAULT 0,
|
||||
remind_nuts INTEGER NOT NULL DEFAULT 0,
|
||||
show_meal_balancing INTEGER NOT NULL DEFAULT 1,
|
||||
suggest_templates INTEGER NOT NULL DEFAULT 1,
|
||||
suggest_patterns INTEGER NOT NULL DEFAULT 1,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
endpoint TEXT NOT NULL UNIQUE,
|
||||
p256dh TEXT NOT NULL,
|
||||
auth TEXT NOT NULL,
|
||||
user_agent TEXT,
|
||||
is_active INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_test_at TEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
database.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS shopping_needs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
household_id INTEGER NOT NULL,
|
||||
owner_user_id INTEGER,
|
||||
visibility TEXT NOT NULL DEFAULT 'shared',
|
||||
item_id INTEGER NOT NULL,
|
||||
source_item_id INTEGER,
|
||||
needed_for_date TEXT NOT NULL,
|
||||
needed_for_daypart_id INTEGER,
|
||||
activation_date TEXT NOT NULL,
|
||||
is_activated INTEGER NOT NULL DEFAULT 0,
|
||||
activated_at TEXT,
|
||||
created_by INTEGER,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE (item_id, source_item_id, needed_for_date, needed_for_daypart_id, visibility),
|
||||
FOREIGN KEY (household_id) REFERENCES households(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (owner_user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (source_item_id) REFERENCES items(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (needed_for_daypart_id) REFERENCES dayparts(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
if table_exists(database, "plan_entries"):
|
||||
add_column_if_missing(database, "plan_entries", "household_id INTEGER")
|
||||
add_column_if_missing(database, "plan_entries", "owner_user_id INTEGER")
|
||||
@@ -126,10 +214,18 @@ def sync_default_categories(database: sqlite3.Connection) -> None:
|
||||
for sort_order, name in enumerate(DEFAULT_CATEGORIES, start=10):
|
||||
database.execute(
|
||||
"""
|
||||
INSERT OR IGNORE INTO household_categories (household_id, name, sort_order, is_active)
|
||||
VALUES (?, ?, ?, 1)
|
||||
INSERT OR IGNORE INTO household_categories (household_id, name, builder_key, sort_order, is_active)
|
||||
VALUES (?, ?, ?, ?, 1)
|
||||
""",
|
||||
(household_id, name, sort_order),
|
||||
(household_id, name, DEFAULT_CATEGORY_BUILDERS.get(name, "neutral"), sort_order),
|
||||
)
|
||||
database.execute(
|
||||
"""
|
||||
UPDATE household_categories
|
||||
SET builder_key = COALESCE(NULLIF(builder_key, ''), ?)
|
||||
WHERE household_id = ? AND name = ?
|
||||
""",
|
||||
(DEFAULT_CATEGORY_BUILDERS.get(name, "neutral"), household_id, name),
|
||||
)
|
||||
|
||||
|
||||
@@ -141,6 +237,11 @@ def ensure_schema_upgrades(database: sqlite3.Connection) -> None:
|
||||
add_column_if_missing(database, "users", "updated_at TEXT")
|
||||
|
||||
default_household_id = ensure_default_household(database)
|
||||
database.execute("UPDATE households SET shopping_weekday = COALESCE(shopping_weekday, 5)")
|
||||
database.execute("UPDATE households SET shopping_prep_days = COALESCE(shopping_prep_days, 1)")
|
||||
database.execute(
|
||||
"UPDATE households SET shopping_reminder_time = COALESCE(NULLIF(shopping_reminder_time, ''), '18:00')"
|
||||
)
|
||||
database.execute(
|
||||
"UPDATE users SET household_id = ? WHERE household_id IS NULL",
|
||||
(default_household_id,),
|
||||
@@ -204,6 +305,12 @@ def ensure_schema_upgrades(database: sqlite3.Connection) -> None:
|
||||
database.execute("UPDATE plan_entries SET visibility = 'shared' WHERE visibility IS NULL OR visibility = ''")
|
||||
|
||||
sync_default_categories(database)
|
||||
database.execute(
|
||||
"""
|
||||
INSERT OR IGNORE INTO user_settings (user_id)
|
||||
SELECT id FROM users
|
||||
"""
|
||||
)
|
||||
|
||||
database.execute(
|
||||
"""
|
||||
@@ -236,6 +343,12 @@ def ensure_schema_upgrades(database: sqlite3.Connection) -> None:
|
||||
ON shopping_entries (household_id, visibility, is_checked)
|
||||
"""
|
||||
)
|
||||
database.execute(
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_shopping_needs_household_activation
|
||||
ON shopping_needs (household_id, activation_date, is_activated)
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def apply_schema(database: sqlite3.Connection) -> None:
|
||||
|
||||
Reference in New Issue
Block a user