Cookwala / SDK and scenarios / 072
The per-meal nutrients a MENU message carries and the packs read
MENU carries energy, sodium, fruit and vegetables and protein per meal. The school-meals and sodium-reduction packs compare these fields with their limits; the sodium pack validates as a profile document.
Read first: /docs/HUMANITARIAN-PROFILE/ /humanitarian/
Steps
Energy, sodium and fruit-and-vegetable grams become energyKcal, sodiumMg and fruitVegG.
parseSmsProtein becomes proteinG; a message may carry any subset.
parseSmsA MENU with no nutrient is a usage error.
parseSmsA nutrient word without a number is a usage error.
parseSmsThe sodium-reduction rule pack is itself a profile document.
validate
Outcome
You know the four nutrient fields and their names in documents. Next: scenario 077 hashes and validates the rule packs.
Command line
cookwala sms "MENU D12 KCAL650 SODIUM540 FV95"
cat profiles/humanitarian/sodium-reduction.rulepack.jsonRecorded output
Status: ok
[1] parseSms: {"ok": true, "command": "MENU", "distribution": "D12", "perMeal": {"energyKcal": 650.0, "sodiumMg": 540.0, "fruitVegG": 95.0}, "findings": []}
[2] parseSms: {"ok": true, "command": "MENU", "distribution": "D12", "perMeal": {"proteinG": 22.0}, "findings": []}
[3] parseSms: {"ok": false, "error": "usage"}
[4] parseSms: {"ok": false, "error": "usage"}
[5] validate: {"ok": true, "errors": []}Code
Run: python scenarios/out/072-per-meal-nutrients-by-sms/python.py
# Scenario 072: The per-meal nutrients a MENU message carries and the packs read
# MENU carries energy, sodium, fruit and vegetables and protein per meal. The school-meals and sodium-reduction packs compare these fields with their limits; the sodium pack validates as a profile document.
# Run a hub first: python hub/cookwala_hub.py --recipes examples
import json, os
from cookwala.client import CookwalaClient, CookwalaProblem
def load(path):
with open(path, encoding="utf-8") as f:
return json.load(f)
def pick(obj, path):
for part in path.replace("]", "").replace("[", ".").split("."):
obj = obj[int(part)] if part.isdigit() else (len(obj) if part == "length" else obj.get(part))
return obj
c = CookwalaClient(os.environ.get("COOKWALA_HUB", "http://localhost:7878"))
# Step 1: Energy, sodium and fruit-and-vegetable grams become energyKcal, sodiumMg and fruitVegG.
menu = c.parse_sms("MENU D12 KCAL650 SODIUM540 FV95")
assert pick(menu, "perMeal.energyKcal") == 650.0, pick(menu, "perMeal.energyKcal")
print("ok", "perMeal.energyKcal", "=", 650.0)
assert pick(menu, "perMeal.sodiumMg") == 540.0, pick(menu, "perMeal.sodiumMg")
print("ok", "perMeal.sodiumMg", "=", 540.0)
assert pick(menu, "perMeal.fruitVegG") == 95.0, pick(menu, "perMeal.fruitVegG")
print("ok", "perMeal.fruitVegG", "=", 95.0)
# Step 2: Protein becomes proteinG; a message may carry any subset.
protein = c.parse_sms("MENU D12 PROTEIN22")
assert pick(protein, "perMeal.proteinG") == 22.0, pick(protein, "perMeal.proteinG")
print("ok", "perMeal.proteinG", "=", 22.0)
# Step 3: A MENU with no nutrient is a usage error.
usage = c.parse_sms("MENU D12")
assert pick(usage, "ok") == False, pick(usage, "ok")
print("ok", "ok", "=", False)
assert pick(usage, "error") == "usage", pick(usage, "error")
print("ok", "error", "=", "usage")
# Step 4: A nutrient word without a number is a usage error.
usage2 = c.parse_sms("MENU D12 KCAL")
assert pick(usage2, "ok") == False, pick(usage2, "ok")
print("ok", "ok", "=", False)
assert pick(usage2, "error") == "usage", pick(usage2, "error")
print("ok", "error", "=", "usage")
# Step 5: The sodium-reduction rule pack is itself a profile document.
pack = c.validate("humanitarian", load("profiles/humanitarian/sodium-reduction.rulepack.json"))
assert pick(pack, "ok") == True, pick(pack, "ok")
print("ok", "ok", "=", True)
print("scenario complete")