plate VIII.I · live demo
i / iii
world weather
sixteen beloved cities, polled daily by a cr8script in CI · the snapshot is committed to this repo
loading… reading weather-data.json
plate VIII.II · aggregate
ii / iii
at a glance
summary across the cities reporting
waiting on the live fetch…
plate VIII.III · the same in cr8script
iii / iii
the same flow, in cr8
this page renders via JS, with the exact polling script shown below
The snapshot above is produced by a cr8script running in this
site's CI. It pulls all sixteen cities, aggregates with pipelines,
and emits one JSON document on stdout. A daily cron redirects that
to weather-data.json and commits it. The page just
reads the committed file -- no third-party calls from your browser,
no key, no proxy.
# poll_weather.cr8 -- emits a single JSON document on stdout containing # current weather + aggregates for beloved world cities. Driven by the # .github/workflows/poll-weather.yml cron once a day. Intentionally has # no `show` other than the final json.stringify call so the workflow can # redirect stdout straight into weather-data.json. # # pip install cr8script # cr8script tools/poll_weather.cr8 > weather-data.json # # Source: Open-Meteo (https://open-meteo.com) -- free, no API key. let cities = [ { name: "Paris", lat: 48.8566, lon: 2.3522 }, { name: "London", lat: 51.5072, lon: -0.1276 }, { name: "New York", lat: 40.7128, lon: -74.0060 }, { name: "Tokyo", lat: 35.6762, lon: 139.6503 }, { name: "Rome", lat: 41.9028, lon: 12.4964 }, { name: "Barcelona", lat: 41.3874, lon: 2.1686 }, { name: "Sydney", lat: -33.8688, lon: 151.2093 }, { name: "Istanbul", lat: 41.0082, lon: 28.9784 }, { name: "Cape Town", lat: -33.9249, lon: 18.4241 }, { name: "Rio de Janeiro", lat: -22.9068, lon: -43.1729 }, { name: "Bangkok", lat: 13.7563, lon: 100.5018 }, { name: "Singapore", lat: 1.3521, lon: 103.8198 }, { name: "Dubai", lat: 25.2048, lon: 55.2708 }, { name: "Buenos Aires", lat: -34.6037, lon: -58.3816 }, { name: "Vancouver", lat: 49.2827, lon: -123.1207 }, { name: "Marrakech", lat: 31.6295, lon: -7.9811 } ] let base = "https://api.open-meteo.com/v1/forecast" let qs = "¤t=temperature_2m,wind_speed_10m,relative_humidity_2m&temperature_unit=fahrenheit&wind_speed_unit=mph" var rows = [] var failures = 0 for each c in cities let url = base + f"?latitude={c.lat}&longitude={c.lon}" + qs let r = http.get(url) let healthy = r.ok and r.status is at least 200 and r.status is less than 300 if healthy is false then failures = failures + 1 else let body = json.parse(r.body) let cur = body.current rows = rows + [{ name: c.name, lat: c.lat, lon: c.lon, temp_f: cur.temperature_2m, humidity: cur.relative_humidity_2m, wind_mph: cur.wind_speed_10m }] end end to compute_aggregate(rows) let temps = rows | map temp_f let winds = rows | map wind_mph let hums = rows | map humidity let hottest = (rows | sort by temp_f descending | take 1).first let coldest = (rows | sort by temp_f ascending | take 1).first let windy = (rows | sort by wind_mph descending | take 1).first return { n_reporting: length(rows), avg_temp_f: math.round(average(temps) * 10) / 10, min_temp_f: min(temps), max_temp_f: max(temps), avg_humidity: math.round(average(hums)), avg_wind_mph: math.round(average(winds) * 10) / 10, hottest: { name: hottest.name, temp_f: hottest.temp_f }, coldest: { name: coldest.name, temp_f: coldest.temp_f }, windiest: { name: windy.name, wind_mph: windy.wind_mph } } end let aggregate = if length(rows) is at least 1 then compute_aggregate(rows) else nothing end let payload = { fetched_at_unix: time.now(), source: "open-meteo.com", region: "Beloved world cities", target_city_count: length(cities), failures: failures, cities: rows, aggregate: aggregate } show json.stringify(payload)