Release Nouri 1.3.3 with shopping articles
This commit is contained in:
+102
-23
@@ -57,7 +57,7 @@ from .constants import (
|
||||
WEEKDAY_OPTIONS,
|
||||
WEEK_TEMPLATE_NAME_SUGGESTIONS,
|
||||
)
|
||||
from .db import get_db
|
||||
from .db import get_db, infer_food_flavor_profile, infer_food_profile
|
||||
from .images import (
|
||||
allowed_image_file,
|
||||
save_photo_with_variants,
|
||||
@@ -1005,6 +1005,10 @@ def normalize_shopping_note(value: str | None) -> str:
|
||||
return " ".join((value or "").strip().split())[:80]
|
||||
|
||||
|
||||
def normalize_new_item_name(value: str | None) -> str:
|
||||
return " ".join((value or "").strip().split())[:120]
|
||||
|
||||
|
||||
def schedule_shopping_need(
|
||||
*,
|
||||
item_id: int,
|
||||
@@ -1298,8 +1302,8 @@ def fetch_items_by_ids(item_ids: list[int]) -> list[dict]:
|
||||
return [items_by_id[item_id] for item_id in normalized_ids if item_id in items_by_id]
|
||||
|
||||
|
||||
def find_shopping_food_by_name(name: str) -> dict | None:
|
||||
normalized_name = name.strip().lower()
|
||||
def find_shopping_item_by_name(name: str) -> dict | None:
|
||||
normalized_name = normalize_new_item_name(name).lower()
|
||||
if not normalized_name:
|
||||
return None
|
||||
row = get_db().execute(
|
||||
@@ -1317,11 +1321,11 @@ def find_shopping_food_by_name(name: str) -> dict | None:
|
||||
FROM items
|
||||
LEFT JOIN users AS owner ON owner.id = items.owner_user_id
|
||||
LEFT JOIN users AS target ON target.id = items.target_user_id
|
||||
WHERE items.kind = 'food'
|
||||
WHERE items.kind IN ('food', 'shopping')
|
||||
AND items.is_archived = 0
|
||||
AND LOWER(items.name) = ?
|
||||
AND {visible_clause('items')}
|
||||
ORDER BY LOWER(items.name), items.id
|
||||
ORDER BY CASE items.kind WHEN 'food' THEN 0 ELSE 1 END, LOWER(items.name), items.id
|
||||
LIMIT 1
|
||||
""",
|
||||
[normalized_name, *visible_params()],
|
||||
@@ -1331,6 +1335,64 @@ def find_shopping_food_by_name(name: str) -> dict | None:
|
||||
return attach_builder_keys(attach_dayparts(describe_records([row])))[0]
|
||||
|
||||
|
||||
def create_shopping_search_item(name: str, kind: str) -> dict:
|
||||
normalized_name = normalize_new_item_name(name)
|
||||
if not normalized_name:
|
||||
raise ValueError("Bitte gib zuerst einen Namen ein.")
|
||||
if kind not in {"food", "shopping"}:
|
||||
raise ValueError("Bitte wähle aus, ob es ein Lebensmittel oder ein Einkaufsartikel ist.")
|
||||
|
||||
existing = find_shopping_item_by_name(normalized_name)
|
||||
if existing is not None:
|
||||
return existing
|
||||
|
||||
if kind == "food":
|
||||
profile = infer_food_profile(normalized_name, "Unsortiert", "neutral")
|
||||
category = "Unsortiert"
|
||||
note = "Aus der Einkaufssuche angelegt. Details später ergänzen."
|
||||
is_quick_added = 1
|
||||
else:
|
||||
profile = {
|
||||
"base_type": "neutral",
|
||||
"suggestion_role": "cooking",
|
||||
"suggestion_priority": "never",
|
||||
"can_be_meal_core": 0,
|
||||
}
|
||||
category = "Einkaufsartikel"
|
||||
note = "Einkaufsartikel ohne Rezeptlogik."
|
||||
is_quick_added = 0
|
||||
|
||||
cursor = get_db().execute(
|
||||
"""
|
||||
INSERT INTO items (
|
||||
household_id, owner_user_id, visibility, kind, name, category,
|
||||
base_type, flavor_profile, suggestion_role, suggestion_priority,
|
||||
can_be_meal_core, energy_density, availability_state, note,
|
||||
is_quick_added, created_by, updated_by
|
||||
)
|
||||
VALUES (?, ?, 'shared', ?, ?, ?, ?, ?, ?, ?, ?, 'neutral', 'idea', ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
current_household_id(),
|
||||
g.user["id"],
|
||||
kind,
|
||||
normalized_name,
|
||||
category,
|
||||
profile["base_type"],
|
||||
infer_food_flavor_profile(normalized_name, category, profile["base_type"], profile["suggestion_role"]),
|
||||
profile["suggestion_role"],
|
||||
profile["suggestion_priority"],
|
||||
profile["can_be_meal_core"],
|
||||
note,
|
||||
is_quick_added,
|
||||
g.user["id"],
|
||||
g.user["id"],
|
||||
),
|
||||
)
|
||||
get_db().commit()
|
||||
return get_item(int(cursor.lastrowid))
|
||||
|
||||
|
||||
def fetch_shopping_entries():
|
||||
rows = get_db().execute(
|
||||
f"""
|
||||
@@ -4390,18 +4452,19 @@ def mark_shopping_entry_checked(entry_id: int) -> dict:
|
||||
"UPDATE shopping_entries SET is_checked = 1, checked_at = CURRENT_TIMESTAMP, checked_by = ? WHERE id = ?",
|
||||
(g.user["id"], entry_id),
|
||||
)
|
||||
get_db().execute(
|
||||
"""
|
||||
UPDATE items
|
||||
SET availability_state = 'home',
|
||||
is_archived = 0,
|
||||
is_quick_added = 0,
|
||||
updated_by = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
""",
|
||||
(g.user["id"], item["id"]),
|
||||
)
|
||||
if item["kind"] != "shopping":
|
||||
get_db().execute(
|
||||
"""
|
||||
UPDATE items
|
||||
SET availability_state = 'home',
|
||||
is_archived = 0,
|
||||
is_quick_added = 0,
|
||||
updated_by = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
""",
|
||||
(g.user["id"], item["id"]),
|
||||
)
|
||||
get_db().commit()
|
||||
return item
|
||||
|
||||
@@ -4495,7 +4558,10 @@ def item_mark_bought(item_id: int):
|
||||
except (ValueError, PermissionError) as exc:
|
||||
flash(str(exc), "error")
|
||||
return redirect(request.referrer or url_for("main.shopping_list"))
|
||||
flash(f"{item['name']} ist jetzt als Zuhause vorhanden markiert.", "success")
|
||||
if item["kind"] == "shopping":
|
||||
flash(f"{item['name']} wurde als eingekauft markiert.", "success")
|
||||
else:
|
||||
flash(f"{item['name']} ist jetzt als Zuhause vorhanden markiert.", "success")
|
||||
return redirect(request.referrer or url_for("main.shopping_list"))
|
||||
|
||||
|
||||
@@ -4528,17 +4594,24 @@ def shopping_list():
|
||||
if request.method == "POST":
|
||||
selected_item_id = request.form.get("item_id", "").strip()
|
||||
item_search = request.form.get("item_search", "").strip()
|
||||
create_as = request.form.get("create_as", "").strip()
|
||||
create_item_name = normalize_new_item_name(request.form.get("create_item_name") or item_search)
|
||||
shopping_note = normalize_shopping_note(request.form.get("shopping_note"))
|
||||
item = None
|
||||
if selected_item_id.isdigit():
|
||||
if create_as in {"food", "shopping"}:
|
||||
try:
|
||||
item = create_shopping_search_item(create_item_name, create_as)
|
||||
except ValueError as exc:
|
||||
flash(str(exc), "error")
|
||||
elif selected_item_id.isdigit():
|
||||
try:
|
||||
item = get_item(int(selected_item_id))
|
||||
except ValueError as exc:
|
||||
flash(str(exc), "error")
|
||||
elif item_search:
|
||||
item = find_shopping_food_by_name(item_search)
|
||||
item = find_shopping_item_by_name(item_search)
|
||||
if item is None:
|
||||
flash("Bitte ein Lebensmittel aus der Suche auswählen.", "error")
|
||||
flash("Bitte einen Treffer auswählen oder den Begriff als Lebensmittel bzw. Einkaufsartikel anlegen.", "error")
|
||||
else:
|
||||
flash("Bitte zuerst etwas auswählen.", "error")
|
||||
|
||||
@@ -4560,7 +4633,10 @@ def shopping_list():
|
||||
|
||||
entries = fetch_shopping_entries()
|
||||
upcoming_entries = fetch_upcoming_shopping_needs()
|
||||
addable_items = fetch_items(kind="food", include_archived=False, include_quick_added=True)
|
||||
addable_items = [
|
||||
item for item in fetch_items(include_archived=False, include_quick_added=True)
|
||||
if item["kind"] in {"food", "shopping"}
|
||||
]
|
||||
household_settings = get_household_settings()
|
||||
shopping_weekday_label = dict(WEEKDAY_OPTIONS).get(household_settings["shopping_weekday"], "gesetzt")
|
||||
return render_template(
|
||||
@@ -4581,7 +4657,10 @@ def shopping_check(entry_id: int):
|
||||
except (ValueError, PermissionError) as exc:
|
||||
flash(str(exc), "error")
|
||||
return redirect(url_with_scroll_position(url_for("main.shopping_list")))
|
||||
flash(f"{item['name']} ist jetzt als Zuhause vorhanden markiert.", "success")
|
||||
if item["kind"] == "shopping":
|
||||
flash(f"{item['name']} wurde als eingekauft markiert.", "success")
|
||||
else:
|
||||
flash(f"{item['name']} ist jetzt als Zuhause vorhanden markiert.", "success")
|
||||
return redirect(url_with_scroll_position(url_for("main.shopping_list")))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user