ledger: openmeteo per-city summary extract (2026-11-02)
This commit is contained in:
1 parent
87ddf66863
commit
6a5d938976
2 files changed
+91
No files matched your search
@@ -0,0 +1,7 @@
|
||||
city,country,temperature_2m_C,apparent_temperature_C,apparent_minus_actual_C,relative_humidity_pct,weather_code,condition,wind_speed_10m_kmh
|
||||
Cape Town,South Africa,16.1,14.1,-2.0,85,1,Mainly clear,21.8
|
||||
London,United Kingdom,8.9,6.9,-2.0,84,2,Partly cloudy,7.9
|
||||
New York,United States,12.7,9.9,-2.8,58,0,Clear sky,10.5
|
||||
Sydney,Australia,20.2,21.0,0.8,74,1,Mainly clear,6.6
|
||||
São Paulo,Brazil,21.7,24.1,2.4,87,3,Overcast,7.0
|
||||
Tokyo,Japan,11.9,11.7,-0.2,91,3,Overcast,2.3
|
||||
|
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify wizard-work/ledger/2026-11-02-openmeteo-summary.csv against the
|
||||
source JSON it was derived from. Exit code is the verdict.
|
||||
|
||||
EXIT CODES
|
||||
0 committed CSV matches the source-derived table (header + all rows)
|
||||
1 mismatch (header, row count, or per-cell value)
|
||||
2 ungradable (source JSON or CSV unreadable/malformed)
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
|
||||
HERE = '/root/troupe-standup/wizard-work/ledger'
|
||||
SRC = HERE + '/openmeteo-source-2026-11-01.json'
|
||||
CSV = HERE + '/2026-11-02-openmeteo-summary.csv'
|
||||
EXPECTED_HEADER = ['city', 'country', 'temperature_2m_C', 'apparent_temperature_C',
|
||||
'apparent_minus_actual_C', 'relative_humidity_pct', 'weather_code',
|
||||
'condition', 'wind_speed_10m_kmh']
|
||||
|
||||
|
||||
def recompute():
|
||||
d = json.load(open(SRC))
|
||||
rows = []
|
||||
for c in sorted(d):
|
||||
g = d[c]['geocode']
|
||||
w = d[c]['weather']
|
||||
rows.append({
|
||||
'city': c,
|
||||
'country': g['country'],
|
||||
'temperature_2m_C': w['temperature_2m'],
|
||||
'apparent_temperature_C': w['apparent_temperature'],
|
||||
'apparent_minus_actual_C': round(w['apparent_temperature'] - w['temperature_2m'], 1),
|
||||
'relative_humidity_pct': w['relative_humidity_2m'],
|
||||
'weather_code': w['weather_code'],
|
||||
'condition': w['weather_desc'],
|
||||
'wind_speed_10m_kmh': w['wind_speed_10m'],
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def close(a, b, tol=1e-9):
|
||||
try:
|
||||
return abs(float(a) - float(b)) <= tol
|
||||
except (TypeError, ValueError):
|
||||
return a == b
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
expected = recompute()
|
||||
except Exception as e:
|
||||
print('UNGRADABLE: cannot recompute from source: ' + str(e))
|
||||
return 2
|
||||
try:
|
||||
with open(CSV) as f:
|
||||
lines = [ln.rstrip('\n') for ln in f]
|
||||
except Exception as e:
|
||||
print('UNGRADABLE: cannot read CSV: ' + str(e))
|
||||
return 2
|
||||
if not lines:
|
||||
print('FAIL: CSV is empty')
|
||||
return 1
|
||||
header = lines[0].split(',')
|
||||
if header != EXPECTED_HEADER:
|
||||
print('FAIL: header mismatch: ' + repr(header))
|
||||
return 1
|
||||
body = [ln.split(',') for ln in lines[1:]]
|
||||
if len(body) != len(expected):
|
||||
print('FAIL: row count %d != expected %d' % (len(body), len(expected)))
|
||||
return 1
|
||||
ok = True
|
||||
for i, (act, exp) in enumerate(zip(body, expected)):
|
||||
for col in EXPECTED_HEADER:
|
||||
if not close(act[EXPECTED_HEADER.index(col)], exp[col]):
|
||||
print('FAIL row %d col %s: %r != %r' % (i, col, act[EXPECTED_HEADER.index(col)], exp[col]))
|
||||
ok = False
|
||||
if ok:
|
||||
print('PASS: %d rows match source-derived table; header exact' % len(body))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user