Files
adaptive-pricing/projects/coulomb-pricing/observatory/api.py
2026-06-22 23:05:05 +02:00

107 lines
3.8 KiB
Python

from __future__ import annotations
import json
from decimal import Decimal
from pathlib import Path
from typing import Any
from .economics import build_liquidity_summary, build_snapshot
from .load import (
default_data_dir,
latest_period,
load_budget,
load_expense_records,
load_market_signals,
load_membership,
load_monthly_ledger,
load_payment_records,
load_pricing_models,
load_product,
load_value_range,
)
from .pricing_context import build_cost_floor, build_market_price_view, build_value_range_view
def _serialize(value: Any) -> Any:
if isinstance(value, Decimal):
return str(value)
if hasattr(value, "__dataclass_fields__"):
return {key: _serialize(getattr(value, key)) for key in value.__dataclass_fields__}
if isinstance(value, list):
return [_serialize(item) for item in value]
if isinstance(value, dict):
return {key: _serialize(item) for key, item in value.items()}
return value
def _load_json_catalog(data_dir: Path, name: str) -> dict:
path = data_dir / "infrastructure" / name
if not path.exists():
return {}
return json.loads(path.read_text(encoding="utf-8"))
def build_dashboard_payload(data_dir: Path | None = None, period: str | None = None) -> dict:
root = data_dir or default_data_dir()
product = load_product(root)
budget = load_budget(root)
models = load_pricing_models(root)
members = load_membership(root)
payments = load_payment_records(root)
expenses = load_expense_records(root)
ledger = load_monthly_ledger(root)
target_period = period or latest_period(ledger)
snapshot = build_snapshot(target_period, product, models, members, payments, ledger)
liquidity = build_liquidity_summary(budget, payments, ledger, target_period)
payment_by_period = {record.period: record for record in payments}
history = []
for month in sorted(ledger, key=lambda row: row.period):
if month.period > target_period:
continue
payment = payment_by_period.get(month.period)
net_payment = payment.net_amount if payment else Decimal("0")
history.append(
{
"period": month.period,
"active_members": month.active_members,
"gross_revenue": month.gross_revenue,
"infrastructure_cost": month.infrastructure_cost,
"payment_processing_cost": month.payment_processing_cost,
"total_platform_cost": month.total_platform_cost,
"net_payment": net_payment,
"net_liquidity": net_payment - month.infrastructure_cost,
}
)
value_range_raw = load_value_range(root)
market_raw = load_market_signals(root)
return _serialize(
{
"design_reference": "https://claude.ai/design/p/fb2eef8c-c1fc-4c75-bff4-3782552e5511",
"period": target_period,
"product": product,
"budget": budget,
"snapshot": snapshot,
"liquidity": liquidity,
"history": history,
"pricing_models": models,
"members": members,
"payments": payments,
"expense_record_count": len(expenses),
"cost_floor": build_cost_floor(snapshot, models),
"value_range": build_value_range_view(value_range_raw, snapshot, product, models),
"market_price": build_market_price_view(market_raw),
"infrastructure": {
"domains": _load_json_catalog(root, "domains.json"),
"virtual_servers": _load_json_catalog(root, "virtual_servers.json"),
"stripe": _load_json_catalog(root, "stripe.json"),
},
}
)
def payload_json(data_dir: Path | None = None, period: str | None = None) -> str:
return json.dumps(build_dashboard_payload(data_dir, period), indent=2)