Files
PuGa/tools/state.py
T
dodoxandClaude Sonnet 5 b24f132210 Add runway tool with auto-detected inbound ships; fix multi-base state tagging
- tools/runway.py: days of runway per material before a base stalls, from simulate()'s real
  net consumption, base storage, plus (by default) any ship currently flying to that planet
- tools/state.py: tag each STORE entry with its planet (FIO AddressableId -> site SiteId) and
  each ship with its live flight origin/destination (keyed by ShipId, not StlFuelStoreId).
  Needed now that a 2nd base exists - storage/ship data was previously unscoped, which would
  have silently mixed bases' inventories together
- tests, docs updated (docs/library.md notes the multi-base tagging as a reusable pattern)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 21:56:18 +02:00

67 lines
3.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Sync empire/state/company.yaml from live FIO (own data via FIO_REST_KEY) and show it.
tools/state.py sync # overwrite live fields (buildings, production efficiency, storage, ships, cash, permits)
tools/state.py show
Requires the FIO extension to have uploaded recently; check `as_of`. Manual keys (company, notes) are preserved."""
import sys, datetime
from collections import Counter
from pathlib import Path
import yaml
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from puga import ROOT, config, fio
P = config.EMPIRE_DIR / "state" / "company.yaml" # gitignored
def sync():
u = fio.me()
P.parent.mkdir(parents=True, exist_ok=True)
st = yaml.safe_load(P.read_text()) if P.exists() else {}
code = (st.get('company') or {}).get('ticker') or config.get('COMPANY_CODE')
if not code:
sys.exit('set COMPANY_CODE=<your company code> in .env (first sync), or put company: {ticker: ..} in empire/state/company.yaml')
company = fio.own(f"/company/code/{code}", ttl=0)
for stale in ("cash_aic",):
st.pop(stale, None)
st["as_of"] = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%MZ")
st["cash"] = {b["Currency"]: b["Amount"] for b in company["Balances"] if b["Amount"]}
sites = fio.own(f"/sites/{u}", ttl=0)
prod = fio.own(f"/production/{u}", ttl=0)
stores = fio.own(f"/storage/{u}", ttl=0)
ships = fio.own(f"/ship/ships/{u}", ttl=0)
flights = fio.own(f"/ship/flights/{u}", ttl=0)
by_ship = {f["ShipId"]: f for f in flights}
st["permits"] = {"used": sites[0]["InvestedPermits"] if sites else 0, "total": sites[0]["MaximumPermits"] if sites else 0}
if st.get("permits_total_override"): # e.g. the HQ screen (base permits x / y) disagrees with FIO's MaximumPermits
st["permits"]["total"] = st["permits_total_override"]
old = {b["planet"]: b for b in st.get("bases", [])}
bases = []
for s in sites:
b = old.get(s["PlanetIdentifier"], {})
b.update(name=s["PlanetName"], planet=s["PlanetIdentifier"], buildings=dict(Counter(x["BuildingTicker"] for x in s["Buildings"])),
avg_condition=round(sum(x["Condition"] for x in s["Buildings"]) / len(s["Buildings"]), 4))
b["production"] = [dict(type=l["Type"], count=l["Capacity"], efficiency=round(l["Efficiency"], 4), queued_orders=len(l["Orders"]))
for l in prod if l["SiteId"] == s["SiteId"]]
bases.append(b)
st["bases"] = bases
site_planet = {s["SiteId"]: s["PlanetIdentifier"] for s in sites} # STORE.AddressableId == a site's SiteId
st["storage"] = [dict(type=x["Type"], name=x["Name"], planet=site_planet.get(x.get("AddressableId")),
weight=f"{x['WeightLoad']:.0f}/{x['WeightCapacity']:.0f}t",
items={i["MaterialTicker"]: i["MaterialAmount"] for i in x["StorageItems"] if i.get("MaterialTicker")})
for x in stores if x["StorageItems"] or x["Type"] == "SHIP_STORE"]
def ship_dest(x):
f = by_ship.get(x.get("ShipId"))
return None if not f else {"origin": f.get("Origin"), "destination": f.get("Destination"), "eta_ms": f.get("ArrivalTimeEpochMs")}
st["ships"] = [dict(name=x["Name"], reg=x["Registration"], mass=x["Mass"], in_flight=bool(x.get("FlightId")), flight=ship_dest(x)) for x in ships]
P.write_text(yaml.safe_dump(st, sort_keys=False, default_flow_style=None, width=140))
print("synced", st["as_of"])
if __name__ == "__main__":
cmd = sys.argv[1] if len(sys.argv) > 1 else "show"
if cmd == "sync":
sync()
print(P.read_text())