Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import List, Optional, Sequence
from .dates_add_days import add_days ← from dates.add-days ^1.0.0 · built alongside by fune
from .hospitality_allergen_matrix_data import ALLERGENS, ALLERGENS_HISTORY, Allergen ← this capability’s own data, compiled from data/allergens.json into the same file by fune build
from .hospitality_allergen_matrix_types import AllergenRow, IngredientAllergens
def _list_in_force(jurisdiction: str, on_date: str) -> List[Allergen]:
rows = [
a
for a in ALLERGENS
if a.jurisdiction == jurisdiction and a.valid_from <= on_date and (a.valid_to is None or on_date <= a.valid_to)
]
if rows:
return sorted(rows, key=lambda a: a.annex_number)
# A history-pruned build may have dropped the list that applied then; say so
# rather than answering with nothing.
if ALLERGENS_HISTORY != "full":
earliest: Optional[str] = None
for a in ALLERGENS:
if a.jurisdiction == jurisdiction and (earliest is None or a.valid_from < earliest):
earliest = a.valid_from
if earliest is not None and on_date < earliest:
raise ValueError(
"no allergen list for %s on %s: this build was installed with history=%s, so it only carries lists from %s"
% (jurisdiction, on_date, ALLERGENS_HISTORY, earliest)
)
raise ValueError("no allergen list for %s on %s" % (jurisdiction, on_date))
def _dedupe(names: List[str]) -> List[str]:
seen: List[str] = []
for name in names:
if name not in seen:
seen.append(name)
return seen
def allergen_matrix(ingredients: Sequence[IngredientAllergens], jurisdiction: str, on_date: str) -> List[AllergenRow]:
"""The regulated allergens in a dish, from what its ingredients declare.
Every allergen on the list gets a row, in the legal order, so a menu or
allergen chart shows "none" as a decision rather than a gap. An allergen any
ingredient contains is "contains"; otherwise one any ingredient may contain
is "may-contain". The sources say which ingredients to check.
"""
add_days(on_date, 0)
allergens = _list_in_force(jurisdiction, on_date)
known = {a.code for a in allergens}
for ingredient in ingredients:
for code in list(ingredient.contains) + list(ingredient.may_contain):
if code not in known:
raise ValueError(
'unknown allergen "%s" in "%s": not on the %s list on %s' % (code, ingredient.name, jurisdiction, on_date)
)
rows: List[AllergenRow] = []
for allergen in allergens:
contains = [i.name for i in ingredients if allergen.code in i.contains]
may = [i.name for i in ingredients if allergen.code in i.may_contain]
rows.append(
AllergenRow(
code=allergen.code,
name=allergen.name,
status="contains" if contains else "may-contain" if may else "none",
sources=_dedupe(contains if contains else may),
)
)
return rows