83 lines
2.7 KiB
Python
83 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Real cross-check: re-fetches the World Bank GDP nominal-2023 API and compares the
|
|
committed gdp_nominal_usd_2023.csv (next to this script) against a fresh computation.
|
|
|
|
Exit code: 0 = artifact matches the live source of truth; 1 = mismatch.
|
|
"""
|
|
import csv
|
|
import json
|
|
import os
|
|
import sys
|
|
import urllib.request
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
CSV_PATH = os.path.join(HERE, "gdp_nominal_usd_2023.csv")
|
|
ISO3_PATH = os.path.join(HERE, "iso3_codes.txt")
|
|
|
|
API_URL = ("https://api.worldbank.org/v2/country/all/indicator/NY.GDP.MKTP.CD"
|
|
"?format=json&date=2023:2023&per_page=1000")
|
|
TOLERANCE_USD = 1_000_000 # 1e6 USD == 0.001B (artifact rounds to 3 decimals)
|
|
|
|
|
|
def fetch_json(url):
|
|
req = urllib.request.Request(url, headers={"User-Agent": "ledger/extract/1.0"})
|
|
with urllib.request.urlopen(req, timeout=40) as r:
|
|
return json.loads(r.read().decode("utf-8"))
|
|
|
|
|
|
def main():
|
|
with open(ISO3_PATH, encoding="utf-8") as f:
|
|
iso3 = {ln.strip() for ln in f if ln.strip()}
|
|
|
|
payload = fetch_json(API_URL)
|
|
records = payload[1] if isinstance(payload, list) and payload else []
|
|
api = []
|
|
for rec in records:
|
|
iso = rec.get("countryiso3code")
|
|
val = rec.get("value")
|
|
if iso in iso3 and val is not None:
|
|
api.append((rec["country"]["value"], iso, float(val)))
|
|
api.sort(key=lambda x: -x[2])
|
|
top20 = api[:20]
|
|
|
|
with open(CSV_PATH, encoding="utf-8") as f:
|
|
rows = [r for r in csv.reader(f) if r]
|
|
if not rows:
|
|
print("FAIL: artifact CSV is empty")
|
|
sys.exit(1)
|
|
data = rows[1:]
|
|
|
|
if len(data) != 20:
|
|
print(f"FAIL: artifact has {len(data)} data rows, expected 20")
|
|
sys.exit(1)
|
|
|
|
ok = True
|
|
for i, (cname, ciso, cval) in enumerate(top20):
|
|
row = data[i]
|
|
try:
|
|
artifact_usd = float(row[3]) * 1e9
|
|
except (ValueError, IndexError):
|
|
artifact_usd = None
|
|
good = (row[1] == cname) and (row[2] == ciso) and (artifact_usd is not None
|
|
and abs(artifact_usd - cval) < TOLERANCE_USD)
|
|
if not good:
|
|
ok = False
|
|
print(f"MISMATCH rank {i+1}: source={cname} {ciso} {cval:.3f}B; "
|
|
f"artifact={row}")
|
|
|
|
values = [float(r[3]) for r in data]
|
|
if values != sorted(values, reverse=True):
|
|
ok = False
|
|
print("FAIL: artifact rows are not in descending GDP order")
|
|
|
|
if ok:
|
|
print(f"CHECK OK: {len(top20)} countries match the live World Bank API "
|
|
f"(US={top20[0][1]}, China={top20[1][1]}).")
|
|
sys.exit(0)
|
|
print("CHECK FAIL")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|