2026-04-23 19:26:12 +02:00
|
|
|
import csv
|
|
|
|
|
import json
|
2026-04-26 13:18:19 +02:00
|
|
|
import math
|
2026-04-23 19:26:12 +02:00
|
|
|
import os
|
|
|
|
|
import pprint
|
|
|
|
|
import re
|
|
|
|
|
import sys
|
2026-04-26 13:18:19 +02:00
|
|
|
|
2026-04-23 19:26:12 +02:00
|
|
|
from collections import defaultdict
|
|
|
|
|
from datetime import timedelta
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
from openpyxl import load_workbook
|
|
|
|
|
from tabulate import tabulate
|
|
|
|
|
from tqdm import tqdm
|
|
|
|
|
|
|
|
|
|
from config import *
|
|
|
|
|
from lib import read_recipes, markdown_link, recipe_url, markdown_alert
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def read_plan():
|
|
|
|
|
wb = load_workbook("data/Planung + Einkauf Brotaufstriche.xlsx")
|
|
|
|
|
sheet = wb.active
|
|
|
|
|
|
|
|
|
|
plan = {}
|
|
|
|
|
|
|
|
|
|
for row in sheet.iter_rows(values_only=True):
|
|
|
|
|
|
|
|
|
|
r_name = row[0]
|
|
|
|
|
# Ansprechpartner*in = row[1]
|
|
|
|
|
r_total_net_weight = row[2]
|
|
|
|
|
# gpn23 = row[3]
|
|
|
|
|
r_serving_net_weight = row[4]
|
|
|
|
|
# (Formula) = row[5]
|
|
|
|
|
r_recipe_url = row[6]
|
|
|
|
|
|
|
|
|
|
# We simply skip over any rows that do not have the recipe url
|
|
|
|
|
if not r_recipe_url:
|
|
|
|
|
continue
|
|
|
|
|
if m := re.match(r"https://.*/(\d+)$", r_recipe_url):
|
|
|
|
|
r_recipe_id = int(m.group(1))
|
|
|
|
|
else:
|
|
|
|
|
continue # skip
|
|
|
|
|
|
|
|
|
|
r_wanted_servings = r_total_net_weight / r_serving_net_weight
|
|
|
|
|
|
|
|
|
|
plan[r_name] = {
|
|
|
|
|
"total_net_weight": r_total_net_weight,
|
|
|
|
|
"serving_net_weight": r_serving_net_weight,
|
|
|
|
|
"recipe_id": r_recipe_id,
|
|
|
|
|
"wanted_servings": r_wanted_servings,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return plan
|
|
|
|
|
|
|
|
|
|
plan = read_plan()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# XXX move to lib
|
|
|
|
|
def grams(ingredient):
|
|
|
|
|
conversion = None
|
2026-04-26 13:18:19 +02:00
|
|
|
warnings = []
|
|
|
|
|
|
|
|
|
|
if not ingredient["unit"] and ingredient["food"]["name"] not in FOOD_NO_UNIT_OK:
|
|
|
|
|
warnings.append(f"🟡 No unit for {ingredient["food"]["name"]}")
|
|
|
|
|
|
|
|
|
|
|
2026-04-23 19:26:12 +02:00
|
|
|
for c in ingredient["conversions"]:
|
|
|
|
|
if c["unit"] in ["g / Gramm", "g"]:
|
|
|
|
|
conversion = c
|
|
|
|
|
if conversion:
|
2026-04-26 13:18:19 +02:00
|
|
|
return conversion.get("amount"), warnings
|
|
|
|
|
else:
|
|
|
|
|
if ingredient["unit"]:
|
|
|
|
|
warnings.append(f"🟡 No conversion to grams for unit {ingredient["unit"]["name"]}")
|
|
|
|
|
return None, warnings
|
2026-04-23 19:26:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
|
|
|
|
recipes = read_recipes(keyword="Frühstück")
|
|
|
|
|
|
|
|
|
|
inventory = {}
|
|
|
|
|
|
|
|
|
|
for recipe in recipes:
|
|
|
|
|
scale_factor = plan[recipe["name"]]["wanted_servings"] / recipe["servings"]
|
|
|
|
|
r_name = recipe["name"]
|
|
|
|
|
for step in recipe["steps"]:
|
|
|
|
|
for ingredient in step["ingredients"]:
|
2026-04-26 13:18:19 +02:00
|
|
|
i_name = ingredient["food"]["name"]
|
2026-04-23 19:26:12 +02:00
|
|
|
if not ingredient.get("food"):
|
|
|
|
|
raise ValueError("No food in ingredient")
|
|
|
|
|
continue
|
2026-04-26 13:18:19 +02:00
|
|
|
g, warnings = grams(ingredient)
|
|
|
|
|
if warnings:
|
|
|
|
|
print(r_name, "-", i_name)
|
|
|
|
|
for w in warnings:
|
|
|
|
|
print(f"- {w}")
|
|
|
|
|
i_grams = scale_factor * (g or 0)
|
2026-04-23 19:26:12 +02:00
|
|
|
i_description = ingredient["food"][
|
|
|
|
|
"description"
|
|
|
|
|
] # XXX probably not what i wanted
|
|
|
|
|
|
|
|
|
|
inventory.setdefault(i_name, []).append((i_grams, r_name))
|
|
|
|
|
|
|
|
|
|
# gross ingredient weight per serving
|
|
|
|
|
r_gross_weight = 0.0
|
|
|
|
|
for step in recipe["steps"]:
|
|
|
|
|
for ingredient in step["ingredients"]:
|
|
|
|
|
if not ingredient.get("food"):
|
|
|
|
|
raise ValueError("No food in ingredient")
|
|
|
|
|
continue
|
2026-04-26 13:18:19 +02:00
|
|
|
r_gross_weight += (grams(ingredient)[0] or 0)
|
2026-04-23 19:26:12 +02:00
|
|
|
r_gross_weight /= recipe["servings"]
|
|
|
|
|
|
|
|
|
|
recipe["total_net_weight"] = plan[r_name]["total_net_weight"]
|
|
|
|
|
recipe["wanted_servings"] = plan[r_name]["wanted_servings"]
|
|
|
|
|
recipe["total_gross_weight"] = r_gross_weight * plan[r_name]["wanted_servings"]
|
|
|
|
|
|
2026-04-26 13:18:19 +02:00
|
|
|
print("---")
|
|
|
|
|
print("tags: GPN24, Fruehstueck")
|
|
|
|
|
print("---")
|
|
|
|
|
print("# GPN24 Frühstück Aufstriche Inventar + Bestellung")
|
2026-04-23 19:26:12 +02:00
|
|
|
print(markdown_alert("automatisch erstellt, nicht editieren..."))
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print("## Rezepte und Portionen")
|
|
|
|
|
|
|
|
|
|
data = []
|
|
|
|
|
for recipe in recipes:
|
|
|
|
|
row = [markdown_link(recipe["name"], recipe_url(recipe["id"])),
|
|
|
|
|
int(recipe["wanted_servings"]),
|
|
|
|
|
int(recipe["total_net_weight"]),
|
|
|
|
|
int(recipe["total_gross_weight"])]
|
|
|
|
|
data.append(row)
|
|
|
|
|
|
|
|
|
|
headers = ["Rezept", "Portionen", "Gewicht Netto (Soll)", "Gewicht Brutto Zutaten"]
|
|
|
|
|
|
|
|
|
|
print(tabulate(data, headers=headers, tablefmt="github"))
|
|
|
|
|
print()
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print("## Zutaten")
|
|
|
|
|
for ingredient, entries in inventory.items():
|
|
|
|
|
total = sum(amount for amount, _ in entries)
|
|
|
|
|
|
|
|
|
|
print(f"### {ingredient}")
|
|
|
|
|
|
|
|
|
|
print(f"- **{total:.1f}g** **Total**")
|
|
|
|
|
for i_grams, r_name in entries:
|
|
|
|
|
print(f"- {i_grams:.1f}g {r_name}")
|
2026-04-26 13:18:19 +02:00
|
|
|
print()
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print("## Für die Bestellung nochmal als Tabelle")
|
|
|
|
|
|
|
|
|
|
table_data = []
|
|
|
|
|
for ingredient, entries in inventory.items():
|
|
|
|
|
total = sum(amount for amount, _ in entries)
|
|
|
|
|
|
|
|
|
|
table_data.append((math.ceil(total), ingredient))
|
|
|
|
|
table_headers = ("Gewicht (g)", "Zutat")
|
|
|
|
|
print(tabulate(table_data, headers=table_headers, tablefmt="github"))
|
|
|
|
|
print()
|
|
|
|
|
print()
|
|
|
|
|
|
2026-04-23 19:26:12 +02:00
|
|
|
|
2026-04-26 13:18:19 +02:00
|
|
|
print("## Notes")
|
|
|
|
|
with open("fruehstueck_bestellung+inventar-notes.md") as notes_fd:
|
|
|
|
|
print(notes_fd.read())
|
2026-04-23 19:26:12 +02:00
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|