Cette page a été traduite par une machine et n'a pas encore été révisée par une personne. L'anglais est la référence ; les corrections sont les bienvenues sur GitHub. GitHub
Cookwala / SDK et scénarios / 068
Check the Cairo food bank's recorded offer against the rule packs
The food-bank-cairo flow records a supermarket offer of tomatoes, yogurt and bread with a fridge reading of 3.8 °C. Check it with the basic pack and with the care pack added, validate it, and parse the SMS the offer came from.
Lire d'abord: /docs/HUMANITARIAN-PROFILE/ /humanitarian/
Étapes
The basic WHO/Codex pack finds nothing: the yogurt reading is under 5 °C and every date mark is in the future.
humanitarianCheckAdding the care pack for vulnerable groups changes nothing for this offer.
humanitarianCheckThe document validates as a Humanitarian Profile document.
validateThe yogurt line as it was sent by SMS, per the offer's note: chilled at 3.8 °C, use by 12 November, no gateway finding.
parseSms
Résultat
You have checked a real example document against two packs and seen a clean result. Next: scenario 070 checks an offer that fails.
Ligne de commande
cookwala humanitarian examples/humanitarian/flows/food-bank-cairo/offer.json
cookwala humanitarian --pack profiles/humanitarian/basic-nutrition-food-safety.rulepack.json --pack profiles/humanitarian/care-vulnerable-groups.rulepack.json examples/humanitarian/flows/food-bank-cairo/offer.json
cookwala sms "OFFER 24KG YOGURT C 3.8C UB1211"Sortie enregistrée
Statut: ok
[1] humanitarianCheck: {"results": [{"id": "of-2026-11-09-012", "kind": "Offer", "findings": []}]}
[2] humanitarianCheck: {"results": [{"id": "of-2026-11-09-012", "kind": "Offer", "findings": []}]}
[3] validate: {"ok": true, "errors": []}
[4] parseSms: {"ok": true, "command": "OFFER", "kg": 24.0, "item": "yogurt", "storage": "chilled", "tempC": 3.8, "dateMark": {"kind": "use_by", "dayMonth": "12-11"}, "foodClasses": ["dairy"], "findings": []}Code
Exécuter: python scenarios/out/068-check-the-cairo-food-banks-offer/python.py
# Scenario 068: Check the Cairo food bank's recorded offer against the rule packs
# The food-bank-cairo flow records a supermarket offer of tomatoes, yogurt and bread with a fridge reading of 3.8 °C. Check it with the basic pack and with the care pack added, validate it, and parse the SMS the offer came from.
# 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: The basic WHO/Codex pack finds nothing: the yogurt reading is under 5 °C and every date mark is in the future.
basic = c.humanitarian_check([{"profile": "0.2.0", "kind": "Offer", "id": "of-2026-11-09-012", "donor": "did:web:supermarket.example", "createdAt": "2026-11-09T17:40:00+02:00", "items": [{"lineId": "1", "name": "Tomatoes", "ingredientId": "cw.ing.tomato", "kg": 80, "storage": "ambient", "origin": "retail", "dateMark": {"kind": "best_before", "date": "2026-11-11"}, "allergens": []}, {"lineId": "2", "name": "Yogurt cups", "gtin": "6221234567890", "kg": 24, "units": 160, "storage": "chilled", "origin": "retail", "dateMark": {"kind": "use_by", "date": "2026-11-12"}, "allergens": ["milk"]}, {"lineId": "3", "name": "Bread", "kg": 30, "storage": "ambient", "origin": "retail", "dateMark": {"kind": "best_before", "date": "2026-11-10"}, "allergens": ["cereals_gluten"]}], "window": {"from": "2026-11-09T18:00:00+02:00", "to": "2026-11-10T08:00:00+02:00"}, "site": {"country": "EG", "admin1": "Cairo", "admin2": "Nasr City", "siteName": "Supermarket loading bay"}, "readings": [{"at": "2026-11-09T17:35:00+02:00", "tempC": 3.8, "method": "fridge_display", "lineId": "2"}], "needs": ["refrigerated_vehicle"], "state": "distributed", "version": 5, "note": "Sent by SMS: OFFER 80KG TOMATOES A BB1111; OFFER 24KG YOGURT C 3.8C UB1211; OFFER 30KG BREAD A BB1011. Gateway grouped them under one offer id."}], ["basic-nutrition-food-safety"])
assert pick(basic, "results[0].id") == "of-2026-11-09-012", pick(basic, "results[0].id")
print("ok", "results[0].id", "=", "of-2026-11-09-012")
assert pick(basic, "results[0].kind") == "Offer", pick(basic, "results[0].kind")
print("ok", "results[0].kind", "=", "Offer")
assert pick(basic, "results[0].findings.length") == 0, pick(basic, "results[0].findings.length")
print("ok", "results[0].findings.length", "=", 0)
# Step 2: Adding the care pack for vulnerable groups changes nothing for this offer.
care = c.humanitarian_check([{"profile": "0.2.0", "kind": "Offer", "id": "of-2026-11-09-012", "donor": "did:web:supermarket.example", "createdAt": "2026-11-09T17:40:00+02:00", "items": [{"lineId": "1", "name": "Tomatoes", "ingredientId": "cw.ing.tomato", "kg": 80, "storage": "ambient", "origin": "retail", "dateMark": {"kind": "best_before", "date": "2026-11-11"}, "allergens": []}, {"lineId": "2", "name": "Yogurt cups", "gtin": "6221234567890", "kg": 24, "units": 160, "storage": "chilled", "origin": "retail", "dateMark": {"kind": "use_by", "date": "2026-11-12"}, "allergens": ["milk"]}, {"lineId": "3", "name": "Bread", "kg": 30, "storage": "ambient", "origin": "retail", "dateMark": {"kind": "best_before", "date": "2026-11-10"}, "allergens": ["cereals_gluten"]}], "window": {"from": "2026-11-09T18:00:00+02:00", "to": "2026-11-10T08:00:00+02:00"}, "site": {"country": "EG", "admin1": "Cairo", "admin2": "Nasr City", "siteName": "Supermarket loading bay"}, "readings": [{"at": "2026-11-09T17:35:00+02:00", "tempC": 3.8, "method": "fridge_display", "lineId": "2"}], "needs": ["refrigerated_vehicle"], "state": "distributed", "version": 5, "note": "Sent by SMS: OFFER 80KG TOMATOES A BB1111; OFFER 24KG YOGURT C 3.8C UB1211; OFFER 30KG BREAD A BB1011. Gateway grouped them under one offer id."}], ["basic-nutrition-food-safety", "care-vulnerable-groups"])
assert pick(care, "results[0].findings.length") == 0, pick(care, "results[0].findings.length")
print("ok", "results[0].findings.length", "=", 0)
# Step 3: The document validates as a Humanitarian Profile document.
valid = c.validate("humanitarian", load("examples/humanitarian/flows/food-bank-cairo/offer.json"))
assert pick(valid, "ok") == True, pick(valid, "ok")
print("ok", "ok", "=", True)
assert pick(valid, "errors.length") == 0, pick(valid, "errors.length")
print("ok", "errors.length", "=", 0)
# Step 4: The yogurt line as it was sent by SMS, per the offer's note: chilled at 3.8 °C, use by 12 November, no gateway finding.
yogurt = c.parse_sms("OFFER 24KG YOGURT C 3.8C UB1211")
assert pick(yogurt, "tempC") == 3.8, pick(yogurt, "tempC")
print("ok", "tempC", "=", 3.8)
assert pick(yogurt, "dateMark.dayMonth") == "12-11", pick(yogurt, "dateMark.dayMonth")
print("ok", "dateMark.dayMonth", "=", "12-11")
assert pick(yogurt, "findings.length") == 0, pick(yogurt, "findings.length")
print("ok", "findings.length", "=", 0)
print("scenario complete")