generated from coulomb/repo-seed
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.
55 lines
2.0 KiB
Python
55 lines
2.0 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
from . import _io
|
|
|
|
BUBBLE_STATUS = {"Active": "active", "Cancelled": "churned", "Paused": "paused"}
|
|
|
|
|
|
def import_membership(export: dict, plan_id: str = "flat-899-eur-monthly") -> dict:
|
|
members = []
|
|
for index, user in enumerate(export.get("users", []), start=1):
|
|
bubble_id = user.get("bubble_id") or user.get("_id") or f"bubble-{index}"
|
|
username = user.get("username") or user.get("email") or bubble_id
|
|
status = BUBBLE_STATUS.get(user.get("status", "Active"), "active")
|
|
members.append(
|
|
{
|
|
"id": f"member-{username}",
|
|
"username": username,
|
|
"external_id": bubble_id,
|
|
"status": status,
|
|
"joined_at": user.get("created") or user.get("joined_at"),
|
|
"plan_id": user.get("plan") or plan_id,
|
|
"source": "bubble",
|
|
"churned_at": user.get("cancelled_at") if status == "churned" else None,
|
|
}
|
|
)
|
|
return {
|
|
"version": 1,
|
|
"snapshot_date": export.get("exported_at", export.get("snapshot_date")),
|
|
"members": members,
|
|
"note": "Imported from Bubble export",
|
|
}
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(description="Import Bubble membership export")
|
|
parser.add_argument("--input", type=Path, required=True, help="Bubble JSON export")
|
|
parser.add_argument(
|
|
"--output",
|
|
type=Path,
|
|
default=Path(__file__).resolve().parent.parent.parent / "data" / "membership.json",
|
|
)
|
|
parser.add_argument("--plan-id", default="flat-899-eur-monthly")
|
|
args = parser.parse_args(argv)
|
|
|
|
payload = import_membership(_io.read_export(args.input), args.plan_id)
|
|
_io.write_registry(args.output, payload)
|
|
print(f"Wrote {len(payload['members'])} members → {args.output}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main()) |