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.

Audience: dietitian · Goal: Make people healthier · Level: beginner

Read first: /docs/HUMANITARIAN-PROFILE/ /humanitarian/

Steps

  1. Energy, sodium and fruit-and-vegetable grams become energyKcal, sodiumMg and fruitVegG. parseSms

  2. Protein becomes proteinG; a message may carry any subset. parseSms

  3. A MENU with no nutrient is a usage error. parseSms

  4. A nutrient word without a number is a usage error. parseSms

  5. The 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.json

Recorded 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")

scenarios/072-per-meal-nutrients-by-sms.json

Previous: 071 Next: 073