From 546ef63289a47cdf6a0f39c44a2af189e9c3964c Mon Sep 17 00:00:00 2001 From: Emi Vasilek Date: Sat, 11 Nov 2023 13:49:00 +0100 Subject: [PATCH] rename pricedb to prices --- recipes.py | 104 +++++++++++++++++++++++++++++---------- schemas/ingredients.json | 2 +- templates/recipe.html | 4 +- 3 files changed, 80 insertions(+), 30 deletions(-) diff --git a/recipes.py b/recipes.py index 993ca0b..12dcc37 100644 --- a/recipes.py +++ b/recipes.py @@ -47,20 +47,14 @@ class Element: class Conversion(Element): def load(self, dct: Dict[str, Any]) -> None: - def find_unit(name: str) -> Optional[Unit]: - for unit in self.ctx.units.units: - if unit["name"] == name: - return unit - return None - - fromunit = find_unit(dct["from"]) + fromunit = self.ctx.units.get(dct["from"]) if fromunit is None: - raise RuntimeError(f"unit {fromunit} doesn't exist") + raise RuntimeError(f"unit {dct['from']} doesn't exist") self["from"] = fromunit - tounit = find_unit(dct["to"]) + tounit = self.ctx.units.get(dct["to"]) if tounit is None: - raise RuntimeError(f"unit {tounit} doesn't exist") + raise RuntimeError(f"unit {dct['to']} doesn't exist") self["to"] = tounit @@ -69,10 +63,18 @@ class Unit(Element): oldunits = self.ctx.units.units[:] self.ctx.units.units.append(self) + aliases: List[str] = [] + if "aliases" in dct: + for alias in dct["aliases"]: + aliases.append(alias) + self["aliases"] = aliases + self.ctx.units.units = oldunits + + def finish_load(self) -> None: conversions: List[Conversion] = [] - if "conversions" in dct: - for convdct in dct["conversions"]: - if "from" in dct["conversions"]: + if "conversions" in self.dct: + for convdct in self.dct["conversions"]: + if "from" in self.dct["conversions"]: raise RuntimeError( "conversions in units.yaml cannot have a from field, it is automatically assigned from the unit name" ) @@ -81,13 +83,6 @@ class Unit(Element): conversions.append(conversion) self["conversions"] = conversions - aliases: List[str] = [] - if "aliases" in dct: - for alias in dct["aliases"]: - aliases.append(alias) - self["aliases"] = aliases - self.ctx.units.units = oldunits - class Units: def __init__(self, ctx: Context) -> None: @@ -98,6 +93,8 @@ class Units: for unitdct in lst: unit = Unit(self.ctx, unitdct) self.units.append(unit) + for unit in self.units: + unit.finish_load() def get(self, name: str) -> Optional[Unit]: for unit in self.units: @@ -108,10 +105,10 @@ class Units: class Ingredient(Element): def load(self, dct: Dict[str, Any]) -> None: - if "pricedb" in dct: + if "prices" in dct: pricedb = PriceDBs(self.ctx) - self.ctx.issues += pricedb.load(dct["pricedb"]) - self["pricedb"] = pricedb + self.ctx.issues += pricedb.load(dct["prices"]) + self["prices"] = pricedb conversions = [] if "conversions" in dct: @@ -120,6 +117,50 @@ class Ingredient(Element): conversions.append(conversion) self["conversions"] = conversions + def convert( + self, amount: float, unitfrom: Unit, unitto: Unit, scannedunits: List[Unit] = [] + ) -> Optional[float]: + # TODO: this can not solve conversions when we have kg -> g and g -> piece + if unitto == unitfrom: + return amount + conversions = ( + self["conversions"] + unitfrom["conversions"] + unitto["conversions"] + ) + for conv in conversions: + ratio: float = conv["ratio"] + if ( + conv["from"]["name"] == unitfrom["name"] + and conv["to"]["name"] == unitto["name"] + ): + return amount * ratio + if ( + conv["to"]["name"] == unitfrom["name"] + and conv["from"]["name"] == unitto["name"] + ): + return amount / ratio + return None + + def getprice(self, amount: float, unit: Unit) -> Optional[float]: + if "prices" not in self.dct: + return None + prices: List[float] = [] + pricedbs: PriceDBs = self["prices"] + for entry in pricedbs.pricedbs: + assert isinstance(entry, PriceDB) + entryamount: float = entry["amount"] + entryprice: float = entry["price"] + entryunit: Unit = entry["unit"] + if entryunit == unit: + prices.append((amount / entryamount) * entryprice) + else: + newamount = self.convert(amount, unit, entryunit) + if newamount is not None: + prices.append((newamount / entryamount) * entryprice) + if len(prices) == 0: + return None + assert len(prices) == 1 + return prices[0] + class Ingredients: def __init__(self, ctx: Context) -> None: @@ -166,9 +207,10 @@ class PriceDB(Element): class IngredientInstance(Element): def load(self, dct: Dict[str, Any]) -> None: - self["ingredient"] = self.ctx.ingredients.get(dct["name"]) - if self["ingredient"] is None: + ingredient = self.ctx.ingredients.get(dct["name"]) + if ingredient is None: self.ctx.issues.append(f"unknown ingredient {dct['name']}") + self["ingredient"] = ingredient if "amount" not in dct: self["amount"] = 1.0 @@ -187,10 +229,13 @@ class IngredientInstance(Element): alternatives = [] if "or" in dct: for ingdct in dct["or"]: - ingredient = IngredientInstance(self.ctx, ingdct) - alternatives.append(ingredient) + ing = IngredientInstance(self.ctx, ingdct) + alternatives.append(ing) self["alternatives"] = alternatives + if ingredient is not None: + self["price"] = ingredient.getprice(self["amount"], self["unit"]) + class Recipe(Element): def __init__(self, ctx: Context, dct: Dict[str, Any]) -> None: @@ -225,6 +270,10 @@ class Builder: out = str(input) if out.endswith(".0"): return out.split(".", maxsplit=1)[0] + return out + + def amountprint(input: int) -> str: + out = numprint(input) if out == "0.5": return "1/2" if out == "0.25": @@ -234,6 +283,7 @@ class Builder: return out self.jinjaenv.filters["numprint"] = numprint + self.jinjaenv.filters["amountprint"] = amountprint self.ctx = Context() # list of output files that will be built self.outfiles: Set[str] = set() diff --git a/schemas/ingredients.json b/schemas/ingredients.json index 9265088..a0257d2 100644 --- a/schemas/ingredients.json +++ b/schemas/ingredients.json @@ -11,7 +11,7 @@ "properties": { "name": { "type": "string" }, "wdid": { "type": "integer" }, - "pricedb": { + "prices": { "type": "array", "items": { "type": "object", diff --git a/templates/recipe.html b/templates/recipe.html index 911687b..ba894ae 100644 --- a/templates/recipe.html +++ b/templates/recipe.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% macro ingredientpart(ing) -%} -{{ing.amount|numprint}} {{ing["unit"].name}} {{ ing.name }} +{% if ing.price != None %}{{ing.price|round(1)|numprint}}{%else%} ?{%endif%} {{ing.amount|amountprint}} {{ing["unit"].name}} {{ ing.name }} {%- endmacro %} {% macro ingredient(ing) -%} @@ -36,4 +36,4 @@ {%block body %} back {{getrecipe(recipe)}} -{%endblock%} \ No newline at end of file +{%endblock%}