Files
adaptive-pricing/projects/coulomb-pricing/observatory/load.py
tegwick a1a4aa972f Implement ADAPTIVE-WP-0002 Sprint 1 economic foundations
Add Coulomb observatory package with JSON registries (product, pricing
models, costs, revenue, membership), economics snapshot engine, Economics
Dashboard v1 CLI, sample report, and pytest coverage. Complete T01 and
queue Sprint 2 Bubble.io integration.
2026-06-22 01:32:48 +02:00

103 lines
3.1 KiB
Python

from __future__ import annotations
import json
from decimal import Decimal
from pathlib import Path
from .models import (
CostEntry,
MembershipRecord,
PricingModel,
Product,
RevenueEntry,
)
def _money(value: str | int | float) -> Decimal:
return Decimal(str(value))
def _read_json(path: Path) -> dict:
return json.loads(path.read_text(encoding="utf-8"))
def default_data_dir() -> Path:
return Path(__file__).resolve().parent.parent / "data"
def load_product(data_dir: Path | None = None) -> Product:
raw = _read_json((data_dir or default_data_dir()) / "product.json")
return Product(
id=raw["id"],
name=raw["name"],
lifecycle_phase=raw["lifecycle_phase"],
currency=raw["currency"],
description=raw["description"],
active_pricing_model_id=raw["active_pricing_model_id"],
)
def load_pricing_models(data_dir: Path | None = None) -> list[PricingModel]:
raw = _read_json((data_dir or default_data_dir()) / "pricing-models.json")
return [
PricingModel(
id=item["id"],
name=item["name"],
model_type=item["model_type"],
lifecycle_phase=item["lifecycle_phase"],
currency=item["currency"],
access_fee_amount=_money(item["access_fee_amount"]),
access_fee_cadence=item["access_fee_cadence"],
status=item["status"],
)
for item in raw["models"]
]
def load_costs(data_dir: Path | None = None) -> tuple[str, list[CostEntry], dict[str, Decimal]]:
raw = _read_json((data_dir or default_data_dir()) / "costs.json")
entries = [
CostEntry(
id=item["id"],
name=item["name"],
category=item["category"],
amount=_money(item["amount"]),
currency=item["currency"],
cadence=item["cadence"],
allocation=item["allocation"],
)
for item in raw["entries"]
]
fx = {pair: _money(rate) for pair, rate in raw.get("fx_rates", {}).items()}
return raw.get("period", ""), entries, fx
def load_revenue(data_dir: Path | None = None) -> list[RevenueEntry]:
raw = _read_json((data_dir or default_data_dir()) / "revenue.json")
return [
RevenueEntry(
id=item["id"],
period=item["period"],
gross_amount=_money(item["gross_amount"]),
fees_amount=_money(item["fees_amount"]),
refunds_amount=_money(item.get("refunds_amount", "0")),
net_amount=_money(item["net_amount"]),
currency=item["currency"],
source=item["source"],
)
for item in raw["entries"]
]
def load_membership(data_dir: Path | None = None) -> list[MembershipRecord]:
raw = _read_json((data_dir or default_data_dir()) / "membership.json")
return [
MembershipRecord(
id=item["id"],
status=item["status"],
joined_at=item["joined_at"],
plan_id=item["plan_id"],
churned_at=item.get("churned_at"),
)
for item in raw["members"]
]