Cookwala / حزمة التطوير والسيناريوهات / 076
Temperature readings decide: one offer passes, one is blocked
The example offer carries readings of 4.1 °C on yogurt and 66 °C on hot rice and passes. The disaster-kitchen offer carries 52 °C on hot rice and is blocked. The same checks run on an SMS before any document exists.
اقرأ أولًا: /docs/HUMANITARIAN-PROFILE/ /humanitarian/
الخطوات
The example offer: chilled and hot-held lines both inside their limits.
humanitarianCheckThe disaster-kitchen offer: hot rice at 52 °C, blocked.
humanitarianCheckThe passing hot line as an SMS: no gateway finding.
parseSmsYogurt at 7 °C as an SMS: the gateway refuses to list it.
parseSms
النتيجة
You know that a recorded reading, not a declaration, is what moves food, and where the limits are written. Next: scenario 077 pins the rule packs by hash.
سطر الأوامر
cookwala humanitarian examples/humanitarian/offer.json examples/humanitarian/flows/disaster-kitchen/offer.json
cookwala sms "OFFER 36KG YOGURT C T7C"المخرجات المسجلة
الحالة: ok
[1] humanitarianCheck: {"results": [{"id": "of-2026-11-02-001", "kind": "Offer", "findings": []}]}
[2] humanitarianCheck: {"results": [{"id": "of-2026-11-03-rel-004", "kind": "Offer", "findings": [{"rule": "safety.hot_hold_min", "severity": "block", "detail": "line 3: 52 °C"}]}]}
[3] parseSms: {"ok": true, "command": "OFFER", "kg": 18.0, "item": "cooked rice trays", "storage": "hot_held", "tempC": 66.0, "foodClasses": ["cooked_food", "cooked_rice"], "findings": []}
[4] parseSms: {"ok": true, "command": "OFFER", "kg": 36.0, "item": "yogurt", "storage": "chilled", "tempC": 7.0, "foodClasses": ["dairy"], "findings": ["safety.chilled_max"]}الكود
التشغيل: python scenarios/out/076-readings-decide-whether-a-line-may-move/python.py
# Scenario 076: Temperature readings decide: one offer passes, one is blocked
# The example offer carries readings of 4.1 °C on yogurt and 66 °C on hot rice and passes. The disaster-kitchen offer carries 52 °C on hot rice and is blocked. The same checks run on an SMS before any document exists.
# 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 example offer: chilled and hot-held lines both inside their limits.
pass = c.humanitarian_check([{"profile": "0.1.0", "kind": "Offer", "id": "of-2026-11-02-001", "donor": "did:web:greengrocer.example", "state": "offered", "version": 1, "items": [{"lineId": "1", "name": "Tomatoes", "ingredientId": "cw.ing.tomato", "kg": 120, "storage": "ambient", "dateMark": {"kind": "best_before", "date": "2026-11-04"}, "allergens": []}, {"lineId": "2", "name": "Red lentils", "ingredientId": "cw.ing.lentils_red", "kg": 200, "storage": "ambient", "dateMark": {"kind": "best_before", "date": "2027-03-01"}, "allergens": []}, {"lineId": "3", "name": "Yogurt cups", "gtin": "6221234567890", "kg": 36, "units": 240, "storage": "chilled", "dateMark": {"kind": "use_by", "date": "2026-11-05"}, "allergens": ["milk"]}, {"lineId": "4", "name": "Cooked rice trays", "kg": 18, "storage": "hot_held", "cooked": True, "cookedAt": "2026-11-02T13:30:00+02:00", "allergens": []}], "window": {"from": "2026-11-02T18:00:00+02:00", "to": "2026-11-03T08:00:00+02:00"}, "site": {"country": "EG", "admin1": "Cairo", "admin2": "Nasr City", "siteName": "Supermarket loading bay", "lat": 30.06, "lon": 31.33}, "readings": [{"at": "2026-11-02T17:55:00+02:00", "tempC": 4.1, "method": "fridge_display", "lineId": "3"}, {"at": "2026-11-02T17:55:00+02:00", "tempC": 66, "method": "probe", "lineId": "4"}], "needs": ["refrigerated_vehicle", "same_day"], "note": "Loading bay closes at 21:00; ring the bell."}], ["basic-nutrition-food-safety"])
assert pick(pass, "results[0].id") == "of-2026-11-02-001", pick(pass, "results[0].id")
print("ok", "results[0].id", "=", "of-2026-11-02-001")
assert pick(pass, "results[0].findings.length") == 0, pick(pass, "results[0].findings.length")
print("ok", "results[0].findings.length", "=", 0)
# Step 2: The disaster-kitchen offer: hot rice at 52 °C, blocked.
block = c.humanitarian_check([{"profile": "0.2.0", "kind": "Offer", "id": "of-2026-11-03-rel-004", "donor": "did:web:wholesaler.example", "createdAt": "2026-11-02T16:00:00+02:00", "items": [{"lineId": "1", "name": "Red lentils", "ingredientId": "cw.ing.lentils_red", "kg": 200, "storage": "ambient", "origin": "wholesale", "dateMark": {"kind": "best_before", "date": "2027-03-01"}, "allergens": []}, {"lineId": "2", "name": "Rice", "ingredientId": "cw.ing.rice_short", "kg": 150, "storage": "ambient", "origin": "wholesale", "dateMark": {"kind": "best_before", "date": "2027-06-01"}, "allergens": []}, {"lineId": "3", "name": "Cooked rice trays", "kg": 18, "storage": "hot_held", "origin": "food_service", "cooked": True, "cookedAt": "2026-11-02T13:30:00+02:00", "allergens": [], "foodClasses": ["cooked_rice"]}], "window": {"from": "2026-11-02T16:30:00+02:00", "to": "2026-11-02T22:00:00+02:00"}, "site": {"country": "EG", "admin1": "Example province", "siteName": "Wholesale depot"}, "readings": [{"at": "2026-11-02T16:25:00+02:00", "tempC": 52, "method": "probe", "lineId": "3"}], "needs": ["same_day"], "state": "distributed", "version": 5}], ["basic-nutrition-food-safety"])
assert pick(block, "results[0].findings[0].rule") == "safety.hot_hold_min", pick(block, "results[0].findings[0].rule")
print("ok", "results[0].findings[0].rule", "=", "safety.hot_hold_min")
assert pick(block, "results[0].findings[0].severity") == "block", pick(block, "results[0].findings[0].severity")
print("ok", "results[0].findings[0].severity", "=", "block")
# Step 3: The passing hot line as an SMS: no gateway finding.
ok = c.parse_sms("OFFER 18KG COOKED RICE TRAYS H T66C")
assert pick(ok, "findings.length") == 0, pick(ok, "findings.length")
print("ok", "findings.length", "=", 0)
# Step 4: Yogurt at 7 °C as an SMS: the gateway refuses to list it.
warm = c.parse_sms("OFFER 36KG YOGURT C T7C")
assert pick(warm, "findings") == ["safety.chilled_max"], pick(warm, "findings")
print("ok", "findings", "=", ["safety.chilled_max"])
print("scenario complete")