Complete Economic Observatory MVP (ADAPTIVE-WP-0002)

Add file-based Bubble, Stripe, and OpenRouter importers; usage attribution,
cost allocation, pricing simulator, credit wallets, and recommendations in the
dashboard API. Document whynot-design UI workflow and archive the finished
workplan with all ten tasks marked done.
This commit is contained in:
2026-06-22 23:23:31 +02:00
parent 04ee6d2421
commit 0a38def5a5
26 changed files with 871 additions and 111 deletions

View File

@@ -0,0 +1,57 @@
from __future__ import annotations
import argparse
from decimal import Decimal
from pathlib import Path
from . import _io
def _money(value: str | int | float) -> str:
return f"{Decimal(str(value)):.2f}"
def import_usage(export: dict, fx_usd_eur: str = "0.92") -> dict:
rate = Decimal(str(fx_usd_eur))
records = []
for index, row in enumerate(export.get("usage", export.get("records", [])), start=1):
cost_usd = Decimal(str(row.get("cost_usd") or row.get("cost", "0")))
cost_eur = (cost_usd * rate).quantize(Decimal("0.01"))
records.append(
{
"id": row.get("id") or f"usage-{row['period']}-{index}",
"period": row["period"],
"member_id": row.get("member_id") or row.get("user_id"),
"model": row["model"],
"tokens": row.get("tokens", 0),
"cost_usd": _money(cost_usd),
"cost_eur": _money(cost_eur),
"source": "openrouter",
}
)
return {
"version": 1,
"fx_usd_eur": str(rate),
"records": sorted(records, key=lambda item: (item["period"], item["id"])),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Import OpenRouter usage export")
parser.add_argument("--input", type=Path, required=True, help="OpenRouter JSON export")
parser.add_argument(
"--output",
type=Path,
default=Path(__file__).resolve().parent.parent.parent / "data" / "usage_records.json",
)
parser.add_argument("--fx-usd-eur", default="0.92")
args = parser.parse_args(argv)
payload = import_usage(_io.read_export(args.input), args.fx_usd_eur)
_io.write_registry(args.output, payload)
print(f"Wrote {len(payload['records'])} usage records → {args.output}")
return 0
if __name__ == "__main__":
raise SystemExit(main())