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.
48 lines
1.6 KiB
Python
48 lines
1.6 KiB
Python
from __future__ import annotations
|
|
|
|
from decimal import Decimal, ROUND_HALF_UP
|
|
from typing import Any
|
|
|
|
TWOPLACES = Decimal("0.01")
|
|
|
|
|
|
def _money(value: Decimal) -> Decimal:
|
|
return value.quantize(TWOPLACES, rounding=ROUND_HALF_UP)
|
|
|
|
|
|
def load_usage_records(data_dir) -> list[dict[str, Any]]:
|
|
from pathlib import Path
|
|
|
|
from .load import _read_json, default_data_dir
|
|
|
|
root = data_dir or default_data_dir()
|
|
path = Path(root) / "usage_records.json"
|
|
if not path.exists():
|
|
return []
|
|
raw = _read_json(path)
|
|
return list(raw.get("records", []))
|
|
|
|
|
|
def build_usage_summary(records: list[dict[str, Any]], period: str) -> dict:
|
|
period_rows = [row for row in records if row.get("period") == period]
|
|
by_member: dict[str, Decimal] = {}
|
|
by_model: dict[str, Decimal] = {}
|
|
total = Decimal("0")
|
|
|
|
for row in period_rows:
|
|
cost = Decimal(str(row.get("cost_eur", "0")))
|
|
total += cost
|
|
member_id = row.get("member_id") or "unknown"
|
|
model = row.get("model") or "unknown"
|
|
by_member[member_id] = by_member.get(member_id, Decimal("0")) + cost
|
|
by_model[model] = by_model.get(model, Decimal("0")) + cost
|
|
|
|
active_members = len(by_member) or 1
|
|
return {
|
|
"period": period,
|
|
"total_ai_spend_eur": _money(total),
|
|
"cost_per_active_user_eur": _money(total / active_members),
|
|
"by_member": {key: _money(value) for key, value in sorted(by_member.items())},
|
|
"by_model": {key: _money(value) for key, value in sorted(by_model.items())},
|
|
"record_count": len(period_rows),
|
|
} |