Files
PuGa/puga/cache.py
T
dodoxandClaude Sonnet 5 3bbf524ebb Add simulator replica, persistence, planet scan, README; split empire state out of the repo
- tools/simulate.py + puga/simulate.py: replica of PRUNplanner's simulator (flows and
  efficiency verified against screenshots), reports real new capex (planned minus built)
- tools/scan.py: staffing variants, freight, HQ/experts, --planet mode, demolish-later,
  --min-n as a pure market-size filter, --json output
- tools/history.py, tools/persistence.py: margin history and short-horizon payback checks
- tools/plan_push.py: guarded delete; tools/state.py: syncs to empire/
- README with features and setup; CLAUDE.md made generic
- Own-empire material (profile, state, plans, notes) moved to gitignored empire/;
  generic examples in plans/examples and state/company.example.yaml

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 00:11:02 +02:00

25 lines
1.1 KiB
Python

"""Tiny disk cache for JSON over HTTP, keyed by URL, expiry by TTL seconds."""
import hashlib, json, time, urllib.error, urllib.request
from .config import CACHE_DIR
UA = "PuGa/0.1 (personal advisory toolkit)"
def get_json(url: str, ttl: int, headers: dict[str, str] | None = None, refresh: bool = False):
CACHE_DIR.mkdir(parents=True, exist_ok=True)
f = CACHE_DIR / (hashlib.sha1(url.encode()).hexdigest()[:16] + ".json")
if not refresh and f.exists() and time.time() - f.stat().st_mtime < ttl:
return json.loads(f.read_text())
req = urllib.request.Request(url, headers={"User-Agent": UA, **(headers or {})})
for attempt in range(4): # transient SSL/connection errors happen on long scans
try:
with urllib.request.urlopen(req, timeout=60) as r:
data = json.load(r)
break
except (urllib.error.URLError, ConnectionError, TimeoutError):
if attempt == 3:
raise
time.sleep(1.5 * (attempt + 1))
f.write_text(json.dumps(data))
return data