Redesign shopping list app, 2-portal home, real-time WebSocket sync, and edit modals

This commit is contained in:
2026-10-15 20:40:41 +02:00
parent 111966c3ae
commit 55b2823113
4 changed files with 1727 additions and 1230 deletions
+60 -6
View File
@@ -335,6 +335,16 @@ def init_db():
except Exception:
pass
try:
cursor.execute("""
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = %s AND TABLE_NAME = 'shopping_list' AND COLUMN_NAME = 'note'
""", (MARIADB_NAME,))
if not cursor.fetchone():
cursor.execute("ALTER TABLE shopping_list ADD COLUMN note VARCHAR(255) DEFAULT '' AFTER category")
except Exception:
pass
cursor.execute("DELETE FROM dishes")
else:
cursor = conn.cursor()
@@ -421,6 +431,8 @@ def init_db():
existing_shop_cols = [c[1] for c in cursor.fetchall()]
if "category" not in existing_shop_cols:
cursor.execute("ALTER TABLE shopping_list ADD COLUMN category TEXT DEFAULT 'Drogerie & Sonstiges'")
if "note" not in existing_shop_cols:
cursor.execute("ALTER TABLE shopping_list ADD COLUMN note TEXT DEFAULT ''")
cursor.execute("DELETE FROM dishes")
conn.commit()
@@ -1039,6 +1051,36 @@ def reset_default_shopping_categories() -> List[Dict[str, Any]]:
return get_shopping_categories()
def update_shopping_item(
item_id: int,
item_name: str,
amount: str = "",
unit: str = "",
category: Optional[str] = None,
note: str = ""
) -> Optional[Dict[str, Any]]:
"""Aktualisiert alle Eigenschaften eines Eintrags in der Einkaufsliste."""
cat = (category or "").strip()
if not cat:
cat = guess_category_for_ingredient(item_name)
query = """
UPDATE shopping_list
SET item_name = ?, amount = ?, unit = ?, category = ?, note = ?
WHERE id = ?
"""
execute_query(query, (item_name.strip(), amount.strip(), unit.strip(), cat, (note or "").strip(), item_id), fetch="none", commit=True)
row = execute_query("SELECT * FROM shopping_list WHERE id = ?", (item_id,), fetch="one")
if row:
row["checked"] = bool(row.get("checked"))
row["display_source"] = clean_source_label(row.get("source", ""))
row["note"] = str(row.get("note") or "").strip()
if isinstance(row.get("created_at"), (datetime, date)):
row["created_at"] = row["created_at"].isoformat()
if isinstance(row.get("updated_at"), (datetime, date)):
row["updated_at"] = row["updated_at"].isoformat()
return row
def update_shopping_item_category(item_id: int, category: str) -> Optional[Dict[str, Any]]:
"""Ändert die Kategorie/den Bereich eines einzelnen Listeneintrags."""
clean_cat = category.strip()
@@ -1048,6 +1090,7 @@ def update_shopping_item_category(item_id: int, category: str) -> Optional[Dict[
if row:
row["checked"] = bool(row.get("checked"))
row["display_source"] = clean_source_label(row.get("source", ""))
row["note"] = str(row.get("note") or "").strip()
if isinstance(row.get("created_at"), (datetime, date)):
row["created_at"] = row["created_at"].isoformat()
if isinstance(row.get("updated_at"), (datetime, date)):
@@ -1058,7 +1101,7 @@ def update_shopping_item_category(item_id: int, category: str) -> Optional[Dict[
def get_shopping_list() -> List[Dict[str, Any]]:
"""Gibt alle Einträge der Einkaufsliste zurück, sortiert nach unvollständig/erledigt."""
query = """
SELECT id, item_name, amount, unit, checked, source, category, created_at, updated_at
SELECT id, item_name, amount, unit, checked, source, category, note, created_at, updated_at
FROM shopping_list
ORDER BY checked ASC, id DESC
"""
@@ -1068,6 +1111,7 @@ def get_shopping_list() -> List[Dict[str, Any]]:
if not r.get("category"):
r["category"] = guess_category_for_ingredient(r.get("item_name", ""))
r["display_source"] = clean_source_label(r.get("source", ""))
r["note"] = str(r.get("note") or "").strip()
if isinstance(r.get("created_at"), (datetime, date)):
r["created_at"] = r["created_at"].isoformat()
if isinstance(r.get("updated_at"), (datetime, date)):
@@ -1075,19 +1119,28 @@ def get_shopping_list() -> List[Dict[str, Any]]:
return rows
def add_shopping_item(item_name: str, amount: str = "", unit: str = "", source: str = "", category: Optional[str] = None) -> Dict[str, Any]:
def add_shopping_item(
item_name: str,
amount: str = "",
unit: str = "",
source: str = "",
category: Optional[str] = None,
note: str = ""
) -> Dict[str, Any]:
"""Fügt einen einzelnen Eintrag zur Einkaufsliste hinzu."""
cat = (category or "").strip()
if not cat:
cat = guess_category_for_ingredient(item_name)
clean_src = clean_source_label(source)
query = "INSERT INTO shopping_list (item_name, amount, unit, source, category, checked) VALUES (?, ?, ?, ?, ?, 0)"
res = execute_query(query, (item_name.strip(), amount.strip(), unit.strip(), clean_src, cat), fetch="none", commit=True)
clean_note = (note or "").strip()
query = "INSERT INTO shopping_list (item_name, amount, unit, source, category, note, checked) VALUES (?, ?, ?, ?, ?, ?, 0)"
res = execute_query(query, (item_name.strip(), amount.strip(), unit.strip(), clean_src, cat, clean_note), fetch="none", commit=True)
item_id = res.get("lastrowid")
row = execute_query("SELECT * FROM shopping_list WHERE id = ?", (item_id,), fetch="one")
if row:
row["checked"] = bool(row.get("checked"))
row["display_source"] = clean_source_label(row.get("source", ""))
row["note"] = str(row.get("note") or "").strip()
if isinstance(row.get("created_at"), (datetime, date)):
row["created_at"] = row["created_at"].isoformat()
if isinstance(row.get("updated_at"), (datetime, date)):
@@ -1111,8 +1164,9 @@ def add_shopping_items_bulk(items: List[Dict[str, Any]]) -> int:
cat = str(it.get("category") or "").strip()
if not cat:
cat = guess_category_for_ingredient(name)
query = "INSERT INTO shopping_list (item_name, amount, unit, source, category, checked) VALUES (?, ?, ?, ?, ?, 0)"
execute_query(query, (name, amt, unit, src, cat), fetch="none", commit=True)
note = str(it.get("note") or "").strip()
query = "INSERT INTO shopping_list (item_name, amount, unit, source, category, note, checked) VALUES (?, ?, ?, ?, ?, ?, 0)"
execute_query(query, (name, amt, unit, src, cat, note), fetch="none", commit=True)
count += 1
return count