overhaul warning and error handling

This commit is contained in:
Emi Vasilek 2023-11-16 00:28:23 +01:00
parent ffa0253193
commit 5d9ddf60b7

View file

@ -1,5 +1,6 @@
from abc import abstractmethod
from collections import defaultdict
import collections
import sys
from typing import Dict, List, Any, Optional, Set
import os
@ -10,6 +11,38 @@ import jinja2
import jsonschema
import jsonschema.exceptions
ISSUE_UNKNOWN_UNIT = "unknown-unit"
ISSUE_UNKNOWN_INGREDIENT = "unknown-ingredient"
ISSUE_DUPLICATE_UNITS = "duplicate-units"
ISSUE_KNOWN_PRICE_UNKNOWN_CONVERSION = "known-price-unknown-conversion"
class Issue:
def __init__(self, id: str, msg: str) -> None:
self.id = id
self.msg = msg
class Issues:
def __init__(self) -> None:
self.errors: List[Issue] = []
self.warnings: List[Issue] = []
def error(self, id: str, msg: str) -> None:
self.errors.append(Issue(id, msg))
def warn(self, id: str, msg: str) -> None:
self.warnings.append(Issue(id, msg))
def check(self) -> int:
retcode = len(self.errors) != 0
for msg in self.errors:
print(f"ERROR {msg.id}: {msg.msg}")
for msg in self.warnings:
print(f"WARNING {msg.id}: {msg.msg}")
self.errors.clear()
self.warnings.clear()
return retcode
class Context:
def __init__(self) -> None:
@ -17,7 +50,7 @@ class Context:
self.default_unit = Unit(self, {"name": "piece"})
self.units.units.append(self.default_unit)
self.ingredients = Ingredients(self)
self.issues: List[str] = []
self.issues = Issues()
class Element:
@ -103,12 +136,20 @@ class Units:
return unit
return None
def validate(self) -> None:
unitnames = []
for unit in self.units:
unitnames.append(unit["name"])
for unitname, num in collections.Counter(unitnames).items():
if num>1:
self.ctx.issues.error(ISSUE_DUPLICATE_UNITS, f"units.yaml: {unitname} should only have one entry, found {num}")
class Ingredient(Element):
def load(self, dct: Dict[str, Any]) -> None:
if "prices" in dct:
pricedb = PriceDBs(self.ctx)
self.ctx.issues += pricedb.load(dct["prices"])
pricedb.load(dct["prices"])
self["prices"] = pricedb
conversions = []
@ -155,8 +196,8 @@ class Ingredient(Element):
# find path between conversions
path = self.dfs(conversions, unitfrom["name"], unitto["name"])
if path is None:
print(
f'WARNING: {self["name"]} has a known price, but conversion {unitfrom["name"]} -> {unitto["name"]} not known'
self.ctx.issues.warn(ISSUE_KNOWN_PRICE_UNKNOWN_CONVERSION,
f'{self["name"]} has a known price, but conversion {unitfrom["name"]} -> {unitto["name"]} not known'
)
return None
assert len(path) != 0
@ -210,11 +251,10 @@ class PriceDBs:
self.ctx = ctx
self.pricedbs: List[PriceDB] = []
def load(self, lst: List[Any]) -> List[str]:
def load(self, lst: List[Any]) -> None:
for elem in lst:
pricedb = PriceDB(self.ctx, elem)
self.pricedbs.append(pricedb)
return self.ctx.issues
def __repr__(self) -> str:
return self.pricedbs.__repr__()
@ -229,7 +269,7 @@ class PriceDB(Element):
unitstr = dct["unit"]
self["unit"] = self.ctx.units.get(unitstr)
if self["unit"] is None:
self.ctx.issues.append(f"unknown unit {unitstr}")
self.ctx.issues.error(ISSUE_UNKNOWN_UNIT, f"unknown unit {unitstr}")
else:
self["unit"] = self.ctx.default_unit
@ -238,7 +278,7 @@ class IngredientInstance(Element):
def load(self, dct: Dict[str, Any]) -> None:
ingredient = self.ctx.ingredients.get(dct["name"])
if ingredient is None:
self.ctx.issues.append(f"unknown ingredient {dct['name']}")
self.ctx.issues.error(ISSUE_UNKNOWN_INGREDIENT, f"unknown ingredient {dct['name']}")
self["ingredient"] = ingredient
if "amount" not in dct:
@ -248,7 +288,7 @@ class IngredientInstance(Element):
unitstr = dct["unit"]
self["unit"] = self.ctx.units.get(unitstr)
if self["unit"] is None:
self.ctx.issues.append("unknown unit {unitstr}")
self.ctx.issues.error(ISSUE_UNKNOWN_UNIT, "unknown unit {unitstr}")
else:
self["unit"] = self.ctx.default_unit
@ -356,18 +396,17 @@ class Builder:
unitsschema = self.load_file("schemas/units.json")
jsonschema.validate(instance=unitsdct, schema=unitsschema)
self.ctx.units.load(unitsdct)
if len(self.ctx.issues) != 0:
for issue in self.ctx.issues:
print("ERROR in units.yaml:", issue)
retcode = self.ctx.issues.check()
if retcode != 0:
return 1
self.ctx.units.validate()
ingredientsdct = self.load_file(dir + "/ingredients.yaml")
ingredientsschema = self.load_file("schemas/ingredients.json")
jsonschema.validate(instance=ingredientsdct, schema=ingredientsschema)
self.ctx.ingredients.load(ingredientsdct)
if len(self.ctx.issues) != 0:
for issue in self.ctx.issues:
print("ERROR in ingredients.yaml:", issue)
retcode = self.ctx.issues.check()
if retcode != 0:
return 1
return 0
@ -389,11 +428,12 @@ class Builder:
recipe = Recipe(self.ctx, recipedct)
recipe.srcpath = file
recipe.outpath = file[:-5] + ".html"
if self.ctx.issues.check() != 0:
continue
recipes.append(recipe)
if len(self.ctx.issues) != 0:
for issue in self.ctx.issues:
print("ERROR", issue)
retcode = self.ctx.issues.check()
if retcode != 0:
return 1
self.rendertemplate(