✨ table to list servings, gross and net weight
This commit is contained in:
parent
5b6e7510fe
commit
cf6c3a9d6f
5 changed files with 185 additions and 78 deletions
138
fruehstueck_bestellung+inventar.py
Normal file
138
fruehstueck_bestellung+inventar.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import csv
|
||||
import json
|
||||
import os
|
||||
import pprint
|
||||
import re
|
||||
import sys
|
||||
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
|
||||
for c in ingredient["conversions"]:
|
||||
if c["unit"] in ["g / Gramm", "g"]:
|
||||
conversion = c
|
||||
if conversion:
|
||||
return conversion.get("amount")
|
||||
|
||||
|
||||
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"]:
|
||||
if not ingredient.get("food"):
|
||||
raise ValueError("No food in ingredient")
|
||||
continue
|
||||
i_grams = scale_factor * (grams(ingredient) or 0)
|
||||
i_name = ingredient["food"]["name"]
|
||||
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
|
||||
r_gross_weight += (grams(ingredient) or 0)
|
||||
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"]
|
||||
|
||||
|
||||
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}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
import csv
|
||||
import json
|
||||
import os
|
||||
import pprint
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
|
||||
from tqdm import tqdm
|
||||
|
||||
from config import *
|
||||
from lib import read_recipes
|
||||
|
||||
|
||||
# TODO read from csv
|
||||
wanted_servings = {
|
||||
"Aufstrich Zwiebel & Kümmel": 100,
|
||||
"Cashew-Streichkäse": 100,
|
||||
"GPN-Tomatenbutter": 100,
|
||||
"Granatapfelcreme": 100,
|
||||
"Gulaschmarmelade": 100,
|
||||
"Hummus": 100,
|
||||
"Matelade Apfel": 100,
|
||||
"Mungobohnenhummus mit Jalapenos und Zatar": 100,
|
||||
"Rauchige Schwarze Bohnencreme": 100,
|
||||
}
|
||||
|
||||
|
||||
# XXX move to lib
|
||||
def grams(ingredient):
|
||||
conversion = None
|
||||
for c in ingredient["conversions"]:
|
||||
if c["unit"] in ["g / Gramm", "g"]:
|
||||
conversion = c
|
||||
if conversion:
|
||||
return conversion.get("amount")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
recipes = read_recipes(keyword="Frühstück")
|
||||
|
||||
inventory = {}
|
||||
|
||||
for recipe in recipes:
|
||||
scale_factor = wanted_servings[recipe["name"]] / recipe["servings"]
|
||||
r_name = recipe["name"]
|
||||
for step in recipe["steps"]:
|
||||
for ingredient in step["ingredients"]:
|
||||
if not ingredient.get("food"):
|
||||
raise ValueError("No food in ingredient")
|
||||
continue
|
||||
i_grams = scale_factor * (grams(ingredient) or 0)
|
||||
i_name = ingredient["food"]["name"]
|
||||
i_description = ingredient["food"][
|
||||
"description"
|
||||
] # XXX probably not what i wanted
|
||||
|
||||
inventory.setdefault(i_name, []).append((i_grams, r_name))
|
||||
|
||||
print("!!! alert")
|
||||
print("automatisch erstellt, nicht editieren...")
|
||||
print("!!!")
|
||||
|
||||
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}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
11
lib.py
11
lib.py
|
|
@ -19,3 +19,14 @@ def read_recipes(keyword=None):
|
|||
]
|
||||
|
||||
return recipes
|
||||
|
||||
|
||||
def recipe_url(recipe_id: int) -> str:
|
||||
return f"{TANDOOR_URL}/recipe/{recipe_id}"
|
||||
|
||||
|
||||
def markdown_link(text: str, url: str) -> str:
|
||||
return f"[{text}]({url})"
|
||||
|
||||
def markdown_alert(text: str) -> str:
|
||||
return f"::: danger\n{text}\n:::"
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ requires-python = ">=3.14"
|
|||
dependencies = [
|
||||
"click>=8.3.2",
|
||||
"mwclient>=0.11.0",
|
||||
"openpyxl>=3.1.5",
|
||||
"pathvalidate>=3.3.1",
|
||||
"requests>=2.33.1",
|
||||
"tabulate>=0.10.0",
|
||||
"tqdm>=4.67.3",
|
||||
]
|
||||
|
||||
|
|
|
|||
34
uv.lock
generated
34
uv.lock
generated
|
|
@ -73,6 +73,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "et-xmlfile"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpn24-recipes"
|
||||
version = "0.0.0"
|
||||
|
|
@ -80,8 +89,10 @@ source = { virtual = "." }
|
|||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "mwclient" },
|
||||
{ name = "openpyxl" },
|
||||
{ name = "pathvalidate" },
|
||||
{ name = "requests" },
|
||||
{ name = "tabulate" },
|
||||
{ name = "tqdm" },
|
||||
]
|
||||
|
||||
|
|
@ -94,8 +105,10 @@ dev = [
|
|||
requires-dist = [
|
||||
{ name = "click", specifier = ">=8.3.2" },
|
||||
{ name = "mwclient", specifier = ">=0.11.0" },
|
||||
{ name = "openpyxl", specifier = ">=3.1.5" },
|
||||
{ name = "pathvalidate", specifier = ">=3.3.1" },
|
||||
{ name = "requests", specifier = ">=2.33.1" },
|
||||
{ name = "tabulate", specifier = ">=0.10.0" },
|
||||
{ name = "tqdm", specifier = ">=4.67.3" },
|
||||
]
|
||||
|
||||
|
|
@ -132,6 +145,18 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openpyxl"
|
||||
version = "3.1.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "et-xmlfile" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathvalidate"
|
||||
version = "3.3.1"
|
||||
|
|
@ -194,6 +219,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/03/36/76704c4f312257d6dbaae3c959add2a622f63fcca9d864659ce6d8d97d3d/ruff-0.15.9-py3-none-win_arm64.whl", hash = "sha256:0694e601c028fd97dc5c6ee244675bc241aeefced7ef80cd9c6935a871078f53", size = 11005870, upload-time = "2026-04-02T18:17:15.773Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tabulate"
|
||||
version = "0.10.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.67.3"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue