venthrive-api / intel /cli.py
GitHub Actions
Sync from GitHub · 2026-10-21 11:53 UTC
b7c46f4
Raw History Blame Contribute Delete
4.4 kB
"""
Command line for the intel store (run from backend/ with INTEL_DB_PATH set):
py -m intel.cli init create tables, seed sources/streams
py -m intel.cli locations build the state/LGA gazetteer
py -m intel.cli calendar holidays + seasons, 2000 → +10 years
py -m intel.cli places load the Overture extract (run overture_extract first)
py -m intel.cli weather-history [--budget N] [--start YYYY-MM-DD] resumable backfill
py -m intel.cli weather-forecast 16-day forecast at state centres
py -m intel.cli status Layer 1 report (exit 1 if unhealthy)
py -m intel.cli export [--out DIR] CSVs + manifest for the Colab notebooks
py -m intel.cli resolve "TEXT" which location does this text mention?
py -m intel.cli run-daily forecast + history top-up + status, one run id
`run-daily` is what a scheduler will call once one exists (none yet — see the
design doc's "Conflicts").
"""
import argparse
import json
import sys
import uuid
from datetime import date
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
from intel import db # noqa: E402 (env must be loaded first)
EXPORT_DIR = Path(__file__).resolve().parent / "data" / "export"
def _print(obj) -> None:
print(json.dumps(obj, indent=2, default=str))
def main(argv: list[str] | None = None) -> int:
p = argparse.ArgumentParser(prog="intel")
sub = p.add_subparsers(dest="cmd", required=True)
sub.add_parser("init")
sub.add_parser("locations")
sub.add_parser("calendar")
sub.add_parser("places")
wh = sub.add_parser("weather-history")
wh.add_argument("--budget", type=float, default=None)
wh.add_argument("--start", type=date.fromisoformat, default=None)
wh.add_argument("--estimate", action="store_true", help="only print the weighted calls still needed")
sub.add_parser("weather-forecast")
sub.add_parser("status")
ex = sub.add_parser("export")
ex.add_argument("--out", type=Path, default=EXPORT_DIR)
rs = sub.add_parser("resolve")
rs.add_argument("text")
sub.add_parser("run-daily")
args = p.parse_args(argv)
con = db.connect()
run_id = f"{args.cmd}-{uuid.uuid4().hex[:8]}"
try:
if args.cmd == "init":
db.init(con)
_print({"initialised": db.db_path()})
elif args.cmd == "locations":
from intel.locations import build
_print(build(con))
elif args.cmd == "calendar":
from intel.ingest.calendar import ingest
_print(ingest(con, run_id=run_id))
elif args.cmd == "places":
from intel.ingest.places import ingest
_print(ingest(con, run_id=run_id))
elif args.cmd == "weather-history":
from intel.ingest import weather
start = args.start or weather.DEFAULT_HISTORY_START
if args.estimate:
_print({"weighted_calls_needed": round(weather.estimate_backfill_calls(con, start=start), 1)})
else:
_print(weather.backfill_history(con, start=start, budget=args.budget or weather.DEFAULT_RUN_BUDGET, run_id=run_id))
elif args.cmd == "weather-forecast":
from intel.ingest.weather import fetch_forecast
_print(fetch_forecast(con, run_id=run_id))
elif args.cmd == "status":
from intel.freshness import report
r = report(con)
_print(r)
return 0 if r["healthy"] else 1
elif args.cmd == "export":
from intel.export import export
_print(export(con, args.out)["rows"])
elif args.cmd == "resolve":
from intel.locations import resolve
_print({"text": args.text, "location_id": resolve(con, args.text)})
elif args.cmd == "run-daily":
from intel.freshness import report
from intel.ingest import weather
out = {"run_id": run_id, "forecast": weather.fetch_forecast(con, run_id=run_id),
"history": weather.backfill_history(con, run_id=run_id)}
out["status"] = report(con)
_print(out)
return 0 if out["status"]["healthy"] else 1
finally:
con.close()
return 0
if __name__ == "__main__":
sys.exit(main())