"""Analyze dashboard.cpsp.id IoT flocks API for date/day mapping.""" import json import urllib.request from collections import Counter, defaultdict from datetime import datetime, timedelta, timezone BASE = "https://dashboard.cpsp.id/api/iot/flocks/" CYCLE_START = datetime(2026, 5, 22).date() CYCLE_END = datetime(2026, 7, 9).date() TZ = timezone(timedelta(hours=7)) def parse_dt(value: str) -> datetime: return datetime.fromisoformat(value.replace("Z", "+00:00")) def fetch_page(page: int) -> dict: url = f"{BASE}?page={page}" with urllib.request.urlopen(url, timeout=60) as resp: return json.loads(resp.read()) def main() -> None: meta = fetch_page(1) total = meta["count"] page_size = len(meta["results"]) last_page = (total + page_size - 1) // page_size print(f"total={total} page_size={page_size} last_page={last_page}") sample_pages = [1, 2, 3, last_page, last_page - 1] records = [] for page in sample_pages: data = fetch_page(page) records.extend(data["results"]) print(f" fetched page {page}: {len(data['results'])} rows") flock_ids = Counter(r["flock_id"] for r in records) print(f"unique flock_ids in sample: {dict(flock_ids)}") r0 = records[0]["payload"]["data"] coop = r0.get("coop") or {} print(f"coop kode={coop.get('kode')} name floor={r0.get('name')} kandang={r0.get('kandang')}") by_day = defaultdict(list) by_fetch_date = defaultdict(list) for r in records: d = r["payload"]["data"] day = d.get("day") if day is not None: by_day[day].append(r) fetched = r.get("fetched_at") or d.get("lastUpdate") if fetched: cal = parse_dt(fetched).astimezone(TZ).date() by_fetch_date[cal].append(r) print(f"day range: {min(by_day)}..{max(by_day)} ({len(by_day)} unique)") print(f"fetched_at date range: {min(by_fetch_date)}..{max(by_fetch_date)} ({len(by_fetch_date)} unique)") print("\nSample day -> metrics (first record per day):") for day in sorted(by_day): if day not in (1, 2, 3, 34, 35, 36, 48, 49): continue d = by_day[day][0]["payload"]["data"] fetched = by_day[day][0].get("fetched_at", "")[:19] cal = CYCLE_START + timedelta(days=day - 1) print( f" day {day:2d} (cycle cal {cal}) fetched={fetched} " f"wind={d.get('wind')} hum={d.get('humidity')} temp={d.get('actualTemperature')} " f"water={d.get('water')} HSI={d.get('HSI')}" ) # Full scan for one flock: aggregate one row per cycle day using `day` field print("\nScanning all pages for daily rollup by `day` (last snapshot per day)...") daily: dict[int, dict] = {} for page in range(1, last_page + 1): for r in fetch_page(page)["results"]: d = r["payload"]["data"] day = d.get("day") if day is None: continue fetched = r.get("fetched_at") if not fetched: continue ts = parse_dt(fetched) prev = daily.get(day) if prev is None or ts > prev["ts"]: daily[day] = {"ts": ts, "r": r} print(f"Collected {len(daily)} unique cycle days from full API") missing = [d for d in range(1, 50) if d not in daily] print(f"Missing days 1..49: {missing}") print("\nFull cycle rollup (mapped to calendar via start 2026-05-22):") print(f"{'day':>3} {'date':>10} {'wind':>6} {'humidity':>8} {'avg_temp':>8} {'water':>8} {'HSI':>8}") for day in range(1, 50): entry = daily.get(day) cal = CYCLE_START + timedelta(days=day - 1) if not entry: print(f"{day:3d} {cal} — missing —") continue d = entry["r"]["payload"]["data"] print( f"{day:3d} {cal} {d.get('wind', 0):6} {d.get('humidity', 0):8} " f"{d.get('actualTemperature', 0):8} {d.get('water', 0):8} {d.get('HSI', 0):8.2f}" ) if __name__ == "__main__": main()