move from dict based classes to standard classes
This commit is contained in:
parent
3d49bc044d
commit
39d021da46
2 changed files with 247 additions and 141 deletions
386
recipes.py
386
recipes.py
|
@ -2,7 +2,7 @@ from abc import abstractmethod
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import collections
|
import collections
|
||||||
import sys
|
import sys
|
||||||
from typing import Dict, List, Any, Optional, Set
|
from typing import Dict, List, Any, Optional, Self, Set
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class Context:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.settings = Settings()
|
self.settings = Settings()
|
||||||
self.units: AUnits = FakeUnits(self)
|
self.units: AUnits = FakeUnits(self)
|
||||||
self.default_unit = Unit(self, {"name": "piece"})
|
self.default_unit = Unit(self, "piece", [], [])
|
||||||
self.ingredients: AIngredients = FakeIngredients(self)
|
self.ingredients: AIngredients = FakeIngredients(self)
|
||||||
self.issues = Issues()
|
self.issues = Issues()
|
||||||
|
|
||||||
|
@ -87,68 +87,76 @@ class Settings:
|
||||||
|
|
||||||
|
|
||||||
class Element:
|
class Element:
|
||||||
def __init__(self, ctx: Context, dct: Dict[str, Any]) -> None:
|
def __init__(self, ctx: Context) -> None:
|
||||||
self.ctx = ctx
|
self.ctx = ctx
|
||||||
self.dct = dct
|
|
||||||
self.load(dct)
|
|
||||||
for elem in self.dct.values():
|
|
||||||
if isinstance(elem, dict):
|
|
||||||
raise RuntimeError("Something wasn't processed properly")
|
|
||||||
|
|
||||||
def __contains__(self, item: Any) -> bool:
|
@classmethod
|
||||||
return item in self.dct
|
def from_dict(cls, ctx: Context, dct: Dict[str, Any]) -> Self:
|
||||||
|
return cls(ctx)
|
||||||
def __getitem__(self, key: str) -> Any:
|
|
||||||
return self.dct[key]
|
|
||||||
|
|
||||||
def __setitem__(self, key: str, val: Any) -> None:
|
|
||||||
self.dct[key] = val
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return self.dct.__repr__()
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def load(self, dct: Dict[str, Any]) -> None:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Conversion(Element):
|
class Conversion(Element):
|
||||||
def load(self, dct: Dict[str, Any]) -> None:
|
def __init__(
|
||||||
fromunit = self.ctx.units.get(dct["from"])
|
self, ctx: Context, fromunit: "Unit", tounit: "Unit", ratio: float
|
||||||
|
) -> None:
|
||||||
|
super().__init__(ctx)
|
||||||
|
self.fromunit = fromunit
|
||||||
|
self.tounit = tounit
|
||||||
|
self.ratio = ratio
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, ctx: Context, dct: Dict[str, Any]) -> Self:
|
||||||
|
fromunit = ctx.units.get(dct["from"])
|
||||||
if fromunit is None:
|
if fromunit is None:
|
||||||
raise RuntimeError(f"unit {dct['from']} doesn't exist")
|
raise RuntimeError(f"unit {dct['from']} doesn't exist")
|
||||||
self["from"] = fromunit
|
|
||||||
|
|
||||||
tounit = self.ctx.units.get(dct["to"])
|
tounit = ctx.units.get(dct["to"])
|
||||||
if tounit is None:
|
if tounit is None:
|
||||||
raise RuntimeError(f"unit {dct['to']} doesn't exist")
|
raise RuntimeError(f"unit {dct['to']} doesn't exist")
|
||||||
self["to"] = tounit
|
return cls(ctx, fromunit, tounit, dct["ratio"])
|
||||||
|
|
||||||
|
|
||||||
class Unit(Element):
|
class Unit(Element):
|
||||||
def load(self, dct: Dict[str, Any]) -> None:
|
def __init__(
|
||||||
oldunits = self.ctx.units.units[:]
|
self,
|
||||||
self.ctx.units.units.append(self)
|
ctx: Context,
|
||||||
|
name: str,
|
||||||
|
conversions: List[Conversion],
|
||||||
|
aliases: List[str],
|
||||||
|
dct: Optional[Dict[str, Any]] = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(ctx)
|
||||||
|
self.name = name
|
||||||
|
self.conversions = conversions
|
||||||
|
self.aliases = aliases
|
||||||
|
self.dct = dct
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, ctx: Context, dct: Dict[str, Any]) -> Self:
|
||||||
|
# conversions are an empty list which is populated in finish_load
|
||||||
|
# which should be ran when all units are registered
|
||||||
aliases: List[str] = []
|
aliases: List[str] = []
|
||||||
if "aliases" in dct:
|
if "aliases" in dct:
|
||||||
for alias in dct["aliases"]:
|
aliases = dct["aliases"]
|
||||||
aliases.append(alias)
|
|
||||||
self["aliases"] = aliases
|
return cls(ctx, name=dct["name"], conversions=[], aliases=aliases, dct=dct)
|
||||||
self.ctx.units.units = oldunits
|
|
||||||
|
|
||||||
def finish_load(self) -> None:
|
def finish_load(self) -> None:
|
||||||
conversions: List[Conversion] = []
|
if self.dct is None:
|
||||||
if "conversions" in self.dct:
|
return
|
||||||
for convdct in self.dct["conversions"]:
|
if "conversions" not in self.dct:
|
||||||
if "from" in self.dct["conversions"]:
|
return
|
||||||
raise RuntimeError(
|
conversions = []
|
||||||
"conversions in units.yaml cannot have a from field, it is automatically assigned from the unit name"
|
for conv in self.dct["conversions"]:
|
||||||
)
|
if "from" in conv:
|
||||||
convdct["from"] = self["name"]
|
raise RuntimeError(
|
||||||
conversion = Conversion(self.ctx, convdct)
|
"conversions in units.yaml cannot have a from field, it is automatically assigned from the unit name"
|
||||||
conversions.append(conversion)
|
)
|
||||||
self["conversions"] = conversions
|
conv["from"] = self.dct["name"]
|
||||||
|
conv = Conversion.from_dict(self.ctx, conv)
|
||||||
|
conversions.append(conv)
|
||||||
|
|
||||||
|
self.conversions = conversions
|
||||||
|
|
||||||
|
|
||||||
class AUnits:
|
class AUnits:
|
||||||
|
@ -170,9 +178,9 @@ class AUnits:
|
||||||
class FakeUnits(AUnits):
|
class FakeUnits(AUnits):
|
||||||
def get(self, name: str) -> Optional[Unit]:
|
def get(self, name: str) -> Optional[Unit]:
|
||||||
for unit in self.units:
|
for unit in self.units:
|
||||||
if unit["name"] == name:
|
if unit.name == name:
|
||||||
return unit
|
return unit
|
||||||
unit = Unit(self.ctx, {"name": name})
|
unit = Unit(self.ctx, name, [], [])
|
||||||
self.units.append(unit)
|
self.units.append(unit)
|
||||||
return unit
|
return unit
|
||||||
|
|
||||||
|
@ -180,21 +188,21 @@ class FakeUnits(AUnits):
|
||||||
class Units(AUnits):
|
class Units(AUnits):
|
||||||
def load(self, lst: List[Any]) -> None:
|
def load(self, lst: List[Any]) -> None:
|
||||||
for unitdct in lst:
|
for unitdct in lst:
|
||||||
unit = Unit(self.ctx, unitdct)
|
unit = Unit.from_dict(self.ctx, unitdct)
|
||||||
self.units.append(unit)
|
self.units.append(unit)
|
||||||
for unit in self.units:
|
for unit in self.units:
|
||||||
unit.finish_load()
|
unit.finish_load()
|
||||||
|
|
||||||
def get(self, name: str) -> Optional[Unit]:
|
def get(self, name: str) -> Optional[Unit]:
|
||||||
for unit in self.units:
|
for unit in self.units:
|
||||||
if unit["name"] == name or "aliases" in unit and name in unit["aliases"]:
|
if unit.name == name or name in unit.aliases:
|
||||||
return unit
|
return unit
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def validate(self) -> None:
|
def validate(self) -> None:
|
||||||
unitnames = []
|
unitnames = []
|
||||||
for unit in self.units:
|
for unit in self.units:
|
||||||
unitnames.append(unit["name"])
|
unitnames.append(unit.name)
|
||||||
for unitname, num in collections.Counter(unitnames).items():
|
for unitname, num in collections.Counter(unitnames).items():
|
||||||
if num > 1:
|
if num > 1:
|
||||||
self.ctx.issues.error(
|
self.ctx.issues.error(
|
||||||
|
@ -204,18 +212,51 @@ class Units(AUnits):
|
||||||
|
|
||||||
|
|
||||||
class Ingredient(Element):
|
class Ingredient(Element):
|
||||||
def load(self, dct: Dict[str, Any]) -> None:
|
def __init__(
|
||||||
if "prices" in dct:
|
self,
|
||||||
pricedb = PriceDBs(self.ctx)
|
ctx: Context,
|
||||||
pricedb.load(dct["prices"])
|
name: str,
|
||||||
self["prices"] = pricedb
|
wdid: Optional[int],
|
||||||
|
prices: List["PriceDB"],
|
||||||
|
conversions: List[Conversion],
|
||||||
|
aliases: List[str],
|
||||||
|
) -> None:
|
||||||
|
super().__init__(ctx)
|
||||||
|
self.name = name
|
||||||
|
self.wdid = wdid
|
||||||
|
self.prices = prices
|
||||||
|
self.conversions = conversions
|
||||||
|
self.aliases = aliases
|
||||||
|
|
||||||
conversions = []
|
@classmethod
|
||||||
|
def from_dict(cls, ctx: Context, dct: Dict[str, Any]) -> Self:
|
||||||
|
wdid = None
|
||||||
|
if "wdid" in dct:
|
||||||
|
wdid = dct["wdid"]
|
||||||
|
|
||||||
|
prices: List[PriceDB] = []
|
||||||
|
if "prices" in dct:
|
||||||
|
for pricedct in dct["prices"]:
|
||||||
|
prices.append(PriceDB.from_dict(ctx, pricedct))
|
||||||
|
|
||||||
|
conversions: List[Conversion] = []
|
||||||
if "conversions" in dct:
|
if "conversions" in dct:
|
||||||
for convdct in dct["conversions"]:
|
for convdct in dct["conversions"]:
|
||||||
conversion = Conversion(self.ctx, convdct)
|
conversion = Conversion.from_dict(ctx, convdct)
|
||||||
conversions.append(conversion)
|
conversions.append(conversion)
|
||||||
self["conversions"] = conversions
|
|
||||||
|
aliases: List[str] = []
|
||||||
|
if "aliases" in dct:
|
||||||
|
aliases = dct["aliases"]
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
ctx,
|
||||||
|
name=dct["name"],
|
||||||
|
wdid=wdid,
|
||||||
|
prices=prices,
|
||||||
|
conversions=conversions,
|
||||||
|
aliases=aliases,
|
||||||
|
)
|
||||||
|
|
||||||
def dfs(
|
def dfs(
|
||||||
self,
|
self,
|
||||||
|
@ -240,21 +281,21 @@ class Ingredient(Element):
|
||||||
def convert(self, amount: float, unitfrom: Unit, unitto: Unit) -> Optional[float]:
|
def convert(self, amount: float, unitfrom: Unit, unitto: Unit) -> Optional[float]:
|
||||||
conversions: Dict[str, Dict[str, float]] = defaultdict(dict)
|
conversions: Dict[str, Dict[str, float]] = defaultdict(dict)
|
||||||
# construct node tree
|
# construct node tree
|
||||||
convs = self["conversions"]
|
convs = self.conversions
|
||||||
for unit in self.ctx.units.units:
|
for unit in self.ctx.units.units:
|
||||||
convs += unit["conversions"]
|
convs += unit.conversions
|
||||||
for conv in convs:
|
for conv in convs:
|
||||||
fromname = conv["from"]["name"]
|
fromname = conv.fromunit.name
|
||||||
toname = conv["to"]["name"]
|
toname = conv.tounit.name
|
||||||
conversions[fromname][toname] = conv["ratio"]
|
conversions[fromname][toname] = conv.ratio
|
||||||
conversions[toname][fromname] = 1 / conv["ratio"]
|
conversions[toname][fromname] = 1 / conv.ratio
|
||||||
|
|
||||||
# find path between conversions
|
# find path between conversions
|
||||||
path = self.dfs(conversions, unitfrom["name"], unitto["name"])
|
path = self.dfs(conversions, unitfrom.name, unitto.name)
|
||||||
if path is None:
|
if path is None:
|
||||||
self.ctx.issues.warn(
|
self.ctx.issues.warn(
|
||||||
ISSUE_KNOWN_PRICE_UNKNOWN_CONVERSION,
|
ISSUE_KNOWN_PRICE_UNKNOWN_CONVERSION,
|
||||||
f'{self["name"]} has a known price, but conversion {unitfrom["name"]} -> {unitto["name"]} not known',
|
f"{self.name} has a known price, but conversion {unitfrom.name} -> {unitto.name} not known",
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
assert len(path) != 0
|
assert len(path) != 0
|
||||||
|
@ -265,15 +306,13 @@ class Ingredient(Element):
|
||||||
return amount
|
return amount
|
||||||
|
|
||||||
def getprice(self, amount: float, unit: Unit) -> Optional["PriceDB"]:
|
def getprice(self, amount: float, unit: Unit) -> Optional["PriceDB"]:
|
||||||
if "prices" not in self.dct:
|
|
||||||
return None
|
|
||||||
prices: List[PriceDB] = []
|
prices: List[PriceDB] = []
|
||||||
pricedbs: PriceDBs = self["prices"]
|
pricedbs: List[PriceDB] = self.prices
|
||||||
for entry in pricedbs.pricedbs:
|
for entry in pricedbs:
|
||||||
assert isinstance(entry, PriceDB)
|
assert isinstance(entry, PriceDB)
|
||||||
entryamount: float = entry["amount"]
|
entryamount: float = entry.amount
|
||||||
entryprice: float = entry["price"]
|
entryprice: float = entry.price
|
||||||
entryunit: Unit = entry["unit"]
|
entryunit: Unit = entry.unit
|
||||||
price = 0.0
|
price = 0.0
|
||||||
if entryunit == unit:
|
if entryunit == unit:
|
||||||
price = (amount / entryamount) * entryprice
|
price = (amount / entryamount) * entryprice
|
||||||
|
@ -283,12 +322,10 @@ class Ingredient(Element):
|
||||||
price = (pricex / entryamount) * entryprice
|
price = (pricex / entryamount) * entryprice
|
||||||
newentry = PriceDB(
|
newentry = PriceDB(
|
||||||
self.ctx,
|
self.ctx,
|
||||||
{
|
price=price,
|
||||||
"price": price,
|
amount=amount,
|
||||||
"amount": amount,
|
unit=unit,
|
||||||
"unit": unit["name"],
|
currency=entry.currency,
|
||||||
"currency": entry["currency"],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
prices.append(newentry)
|
prices.append(newentry)
|
||||||
if len(prices) == 0:
|
if len(prices) == 0:
|
||||||
|
@ -313,9 +350,9 @@ class AIngredients:
|
||||||
class FakeIngredients(AIngredients):
|
class FakeIngredients(AIngredients):
|
||||||
def get(self, name: str) -> Optional[Ingredient]:
|
def get(self, name: str) -> Optional[Ingredient]:
|
||||||
for ing in self.ingredients:
|
for ing in self.ingredients:
|
||||||
if ing["name"] == name:
|
if ing.name == name:
|
||||||
return ing
|
return ing
|
||||||
ing = Ingredient(self.ctx, {"name": name})
|
ing = Ingredient(self.ctx, name, None, [], [], [])
|
||||||
self.ingredients.append(ing)
|
self.ingredients.append(ing)
|
||||||
return ing
|
return ing
|
||||||
|
|
||||||
|
@ -323,113 +360,164 @@ class FakeIngredients(AIngredients):
|
||||||
class Ingredients(AIngredients):
|
class Ingredients(AIngredients):
|
||||||
def load(self, lst: List[Any]) -> None:
|
def load(self, lst: List[Any]) -> None:
|
||||||
for ingdct in lst:
|
for ingdct in lst:
|
||||||
ing = Ingredient(self.ctx, ingdct)
|
ing = Ingredient.from_dict(self.ctx, ingdct)
|
||||||
self.ingredients.append(ing)
|
self.ingredients.append(ing)
|
||||||
|
|
||||||
def get(self, name: str) -> Optional[Ingredient]:
|
def get(self, name: str) -> Optional[Ingredient]:
|
||||||
for ing in self.ingredients:
|
for ing in self.ingredients:
|
||||||
if ing["name"] == name or "aliases" in ing and name in ing["aliases"]:
|
if ing.name == name or name in ing.aliases:
|
||||||
return ing
|
return ing
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class PriceDBs:
|
|
||||||
def __init__(self, ctx: Context) -> None:
|
|
||||||
self.ctx = ctx
|
|
||||||
self.pricedbs: List[PriceDB] = []
|
|
||||||
|
|
||||||
def load(self, lst: List[Any]) -> None:
|
|
||||||
for elem in lst:
|
|
||||||
pricedb = PriceDB(self.ctx, elem)
|
|
||||||
self.pricedbs.append(pricedb)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return self.pricedbs.__repr__()
|
|
||||||
|
|
||||||
|
|
||||||
class PriceDB(Element):
|
class PriceDB(Element):
|
||||||
def load(self, dct: Dict[str, Any]) -> None:
|
def __init__(
|
||||||
if "amount" not in dct:
|
self,
|
||||||
self["amount"] = 1.0
|
ctx: Context,
|
||||||
|
price: float,
|
||||||
|
amount: float,
|
||||||
|
unit: Unit,
|
||||||
|
currency: Optional[str],
|
||||||
|
) -> None:
|
||||||
|
super().__init__(ctx)
|
||||||
|
self.price = price
|
||||||
|
self.amount = amount
|
||||||
|
self.unit = unit
|
||||||
|
self.currency = currency
|
||||||
|
|
||||||
if "unit" in dct:
|
@classmethod
|
||||||
unitstr = dct["unit"]
|
def from_dict(cls, ctx: Context, dct: Dict[str, Any]) -> Self:
|
||||||
self["unit"] = self.ctx.units.get(unitstr)
|
price = dct["price"]
|
||||||
if self["unit"] is None:
|
amount = dct["amount"]
|
||||||
self.ctx.issues.error(ISSUE_UNKNOWN_UNIT, f"unknown unit {unitstr}")
|
|
||||||
|
unitstr = dct["unit"]
|
||||||
|
unitx = ctx.units.get(unitstr)
|
||||||
|
# uses the default unit if unit is not known (this won't be
|
||||||
|
# visible to the user since rendering will stop after
|
||||||
|
# everything is evaluated)
|
||||||
|
unit = ctx.default_unit
|
||||||
|
if unitx is None:
|
||||||
|
ctx.issues.error(ISSUE_UNKNOWN_UNIT, f"unknown unit {unitstr}")
|
||||||
else:
|
else:
|
||||||
self["unit"] = self.ctx.default_unit
|
unit = unitx
|
||||||
|
|
||||||
if "currency" not in dct:
|
currency = ctx.settings.default_currency
|
||||||
self["currency"] = self.ctx.settings.default_currency
|
if "currency" in dct:
|
||||||
|
currency = dct["currency"]
|
||||||
|
return cls(ctx=ctx, price=price, amount=amount, unit=unit, currency=currency)
|
||||||
|
|
||||||
|
|
||||||
class IngredientInstance(Element):
|
class IngredientInstance(Element):
|
||||||
def load(self, dct: Dict[str, Any]) -> None:
|
def __init__(
|
||||||
ingredient = self.ctx.ingredients.get(dct["name"])
|
self,
|
||||||
|
ctx: Context,
|
||||||
|
name: str,
|
||||||
|
amount: float,
|
||||||
|
unit: Unit,
|
||||||
|
alternatives: List["IngredientInstance"],
|
||||||
|
note: str,
|
||||||
|
price: Optional[PriceDB],
|
||||||
|
) -> None:
|
||||||
|
super().__init__(ctx)
|
||||||
|
self.name = name
|
||||||
|
self.amount = amount
|
||||||
|
self.unit = unit
|
||||||
|
self.alternatives = alternatives
|
||||||
|
self.note = note
|
||||||
|
self.price = price
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, ctx: Context, dct: Dict[str, Any]) -> Self:
|
||||||
|
name = dct["name"]
|
||||||
|
|
||||||
|
ingredient = ctx.ingredients.get(name)
|
||||||
if ingredient is None:
|
if ingredient is None:
|
||||||
self.ctx.issues.error(
|
ctx.issues.error(
|
||||||
ISSUE_UNKNOWN_INGREDIENT, f"unknown ingredient {dct['name']}"
|
ISSUE_UNKNOWN_INGREDIENT, f"unknown ingredient {dct['name']}"
|
||||||
)
|
)
|
||||||
self["ingredient"] = ingredient
|
|
||||||
|
|
||||||
if "amount" not in dct:
|
amount = 1.0
|
||||||
self["amount"] = 1.0
|
if "amount" in dct:
|
||||||
|
amount = dct["amount"]
|
||||||
|
|
||||||
|
unit = ctx.default_unit
|
||||||
if "unit" in dct:
|
if "unit" in dct:
|
||||||
unitstr = dct["unit"]
|
unitstr = dct["unit"]
|
||||||
self["unit"] = self.ctx.units.get(unitstr)
|
unitx = ctx.units.get(unitstr)
|
||||||
if self["unit"] is None:
|
if unitx is None:
|
||||||
self.ctx.issues.error(ISSUE_UNKNOWN_UNIT, "unknown unit {unitstr}")
|
ctx.issues.error(ISSUE_UNKNOWN_UNIT, "unknown unit {unitstr}")
|
||||||
else:
|
else:
|
||||||
self["unit"] = self.ctx.default_unit
|
unit = unitx
|
||||||
|
|
||||||
if "note" not in dct:
|
note = ""
|
||||||
self["note"] = ""
|
if "note" in dct:
|
||||||
|
note = dct["note"]
|
||||||
|
|
||||||
alternatives = []
|
alternatives = []
|
||||||
if "or" in dct:
|
if "or" in dct:
|
||||||
for ingdct in dct["or"]:
|
for ingdct in dct["or"]:
|
||||||
ing = IngredientInstance(self.ctx, ingdct)
|
ing = IngredientInstance.from_dict(ctx, ingdct)
|
||||||
alternatives.append(ing)
|
alternatives.append(ing)
|
||||||
self["alternatives"] = alternatives
|
|
||||||
|
|
||||||
|
price = None
|
||||||
if ingredient is not None:
|
if ingredient is not None:
|
||||||
self["price"] = ingredient.getprice(self["amount"], self["unit"])
|
price = ingredient.getprice(amount, unit)
|
||||||
|
return cls(
|
||||||
|
ctx=ctx,
|
||||||
|
name=name,
|
||||||
|
amount=amount,
|
||||||
|
unit=unit,
|
||||||
|
alternatives=alternatives,
|
||||||
|
note=note,
|
||||||
|
price=price,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Recipe(Element):
|
class Recipe(Element):
|
||||||
def __init__(self, ctx: Context, dct: Dict[str, Any]) -> None:
|
def __init__(
|
||||||
super().__init__(ctx, dct)
|
self,
|
||||||
|
ctx: Context,
|
||||||
|
title: str,
|
||||||
|
ingredients: List[IngredientInstance],
|
||||||
|
subrecipes: List["Recipe"],
|
||||||
|
price: Optional[PriceDB],
|
||||||
|
steps: List[str],
|
||||||
|
) -> None:
|
||||||
|
super().__init__(ctx)
|
||||||
self.srcpath = ""
|
self.srcpath = ""
|
||||||
self.outpath = ""
|
self.outpath = ""
|
||||||
|
self.title = title
|
||||||
|
self.ingredients = ingredients
|
||||||
|
self.subrecipes = subrecipes
|
||||||
|
self.price = price
|
||||||
|
self.steps = steps
|
||||||
|
|
||||||
def load(self, dct: Dict[str, Any]) -> None:
|
@classmethod
|
||||||
|
def from_dict(cls, ctx: Context, dct: Dict[str, Any]) -> Self:
|
||||||
ingredients: List[IngredientInstance] = []
|
ingredients: List[IngredientInstance] = []
|
||||||
if "ingredients" in dct:
|
if "ingredients" in dct:
|
||||||
for ing in dct["ingredients"]:
|
for ingdct in dct["ingredients"]:
|
||||||
ingredient = IngredientInstance(self.ctx, ing)
|
ingredient = IngredientInstance.from_dict(ctx, ingdct)
|
||||||
ingredients.append(ingredient)
|
ingredients.append(ingredient)
|
||||||
self["ingredients"] = ingredients
|
|
||||||
|
|
||||||
subrecipes: List[Recipe] = []
|
subrecipes: List[Recipe] = []
|
||||||
if "subrecipes" in dct:
|
if "subrecipes" in dct:
|
||||||
for partdct in dct["subrecipes"]:
|
for partdct in dct["subrecipes"]:
|
||||||
rp = Recipe(self.ctx, partdct)
|
rp = Recipe.from_dict(ctx, partdct)
|
||||||
subrecipes.append(rp)
|
subrecipes.append(rp)
|
||||||
self["subrecipes"] = subrecipes
|
|
||||||
|
|
||||||
price: Optional[int] = 0
|
price: Optional[PriceDB] = None
|
||||||
|
pricex: float = 0
|
||||||
ingswithprice = 0
|
ingswithprice = 0
|
||||||
ingswithoutprice = 0
|
ingswithoutprice = 0
|
||||||
currency = None
|
currency = None
|
||||||
for ing in ingredients:
|
for ing in ingredients:
|
||||||
if ing["price"] is None:
|
if ing.price is None:
|
||||||
ingswithoutprice += 1
|
ingswithoutprice += 1
|
||||||
continue
|
continue
|
||||||
ingswithprice += 1
|
ingswithprice += 1
|
||||||
price += ing["price"]["price"]
|
pricex += ing.price.price
|
||||||
cur_currency = ing["price"]["currency"]
|
cur_currency = ing.price.currency
|
||||||
if currency is None:
|
if currency is None:
|
||||||
currency = cur_currency
|
currency = cur_currency
|
||||||
elif currency != cur_currency:
|
elif currency != cur_currency:
|
||||||
|
@ -437,9 +525,27 @@ class Recipe(Element):
|
||||||
currency = None
|
currency = None
|
||||||
break
|
break
|
||||||
if currency is None or ingswithoutprice != 0 or len(ingredients) == 0:
|
if currency is None or ingswithoutprice != 0 or len(ingredients) == 0:
|
||||||
self["price"] = None
|
price = None
|
||||||
else:
|
else:
|
||||||
self["price"] = PriceDB(self.ctx, {"price": price, "currency": currency})
|
price = PriceDB(
|
||||||
|
ctx=ctx,
|
||||||
|
price=pricex,
|
||||||
|
amount=1,
|
||||||
|
unit=ctx.default_unit,
|
||||||
|
currency=currency,
|
||||||
|
)
|
||||||
|
|
||||||
|
steps = []
|
||||||
|
if "steps" in dct:
|
||||||
|
steps = dct["steps"]
|
||||||
|
return cls(
|
||||||
|
ctx=ctx,
|
||||||
|
title=dct["title"],
|
||||||
|
ingredients=ingredients,
|
||||||
|
subrecipes=subrecipes,
|
||||||
|
price=price,
|
||||||
|
steps=steps,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Builder:
|
class Builder:
|
||||||
|
@ -533,7 +639,7 @@ class Builder:
|
||||||
continue
|
continue
|
||||||
recipedct = self.load_file(dir + "/recipes/" + file)
|
recipedct = self.load_file(dir + "/recipes/" + file)
|
||||||
jsonschema.validate(instance=recipedct, schema=recipeschema)
|
jsonschema.validate(instance=recipedct, schema=recipeschema)
|
||||||
recipe = Recipe(self.ctx, recipedct)
|
recipe = Recipe.from_dict(self.ctx, recipedct)
|
||||||
recipe.srcpath = file
|
recipe.srcpath = file
|
||||||
recipe.outpath = file[:-5] + ".html"
|
recipe.outpath = file[:-5] + ".html"
|
||||||
if self.ctx.issues.check() != 0:
|
if self.ctx.issues.check() != 0:
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
"required": [ "price", "amount", "unit" ],
|
"required": [ "price", "amount", "unit" ],
|
||||||
"properties": {
|
"properties": {
|
||||||
"price": { "type": "number" },
|
"price": { "type": "number" },
|
||||||
"amount": { "type": "integer" },
|
"amount": { "type": "number" },
|
||||||
"unit": { "type": "string" },
|
"unit": { "type": "string" },
|
||||||
"currency": { "type": "string" }
|
"currency": { "type": "string" }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue