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.
107 lines
No EOL
3.1 KiB
Python
107 lines
No EOL
3.1 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
from .economics import build_snapshot
|
|
from .load import (
|
|
default_data_dir,
|
|
load_costs,
|
|
load_membership,
|
|
load_pricing_models,
|
|
load_product,
|
|
load_revenue,
|
|
)
|
|
from .models import EconomicsSnapshot, PricingModel, Product
|
|
|
|
|
|
def render_dashboard(
|
|
product: Product,
|
|
models: list[PricingModel],
|
|
snapshot: EconomicsSnapshot,
|
|
) -> str:
|
|
active = next(m for m in models if m.id == product.active_pricing_model_id)
|
|
registry_lines = "\n".join(
|
|
f"| {model.id} | {model.name} | {model.model_type} | {model.status} |"
|
|
for model in models
|
|
)
|
|
return f"""# Economics Dashboard v1 — {product.name}
|
|
|
|
**Period:** {snapshot.period}
|
|
**Lifecycle phase:** {product.lifecycle_phase}
|
|
**Active pricing model:** {active.name} ({active.access_fee_amount} {active.currency}/{active.access_fee_cadence})
|
|
|
|
## Key Metrics
|
|
|
|
| Metric | Value |
|
|
|--------|------:|
|
|
| Active members | {snapshot.active_members} |
|
|
| Monthly revenue | {snapshot.monthly_revenue} {snapshot.currency} |
|
|
| Monthly cost | {snapshot.monthly_cost} {snapshot.currency} |
|
|
| Cost per member | {snapshot.cost_per_member} {snapshot.currency} |
|
|
| Gross margin | {snapshot.gross_margin} {snapshot.currency} |
|
|
| Gross margin % | {snapshot.gross_margin_pct}% |
|
|
|
|
_Revenue source: {snapshot.revenue_source}_
|
|
|
|
## Pricing Model Registry
|
|
|
|
| ID | Name | Type | Status |
|
|
|----|------|------|--------|
|
|
{registry_lines}
|
|
|
|
## Registries Loaded
|
|
|
|
- Product model (`data/product.json`)
|
|
- Pricing model registry (`data/pricing-models.json`)
|
|
- Cost registry (`data/costs.json`)
|
|
- Revenue registry (`data/revenue.json`)
|
|
- Membership registry (`data/membership.json`)
|
|
"""
|
|
|
|
|
|
def generate_dashboard(data_dir: Path | None = None, period: str | None = None) -> str:
|
|
root = data_dir or default_data_dir()
|
|
product = load_product(root)
|
|
models = load_pricing_models(root)
|
|
members = load_membership(root)
|
|
revenue = load_revenue(root)
|
|
cost_period, costs, fx_rates = load_costs(root)
|
|
target_period = period or cost_period
|
|
|
|
snapshot = build_snapshot(
|
|
target_period,
|
|
product,
|
|
models,
|
|
members,
|
|
revenue,
|
|
costs,
|
|
fx_rates,
|
|
)
|
|
return render_dashboard(product, models, snapshot)
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(description="Coulomb Social Economics Dashboard v1")
|
|
parser.add_argument("--data-dir", type=Path, default=None, help="Registry data directory")
|
|
parser.add_argument("--period", default=None, help="Reporting period (YYYY-MM)")
|
|
parser.add_argument(
|
|
"--output",
|
|
type=Path,
|
|
default=None,
|
|
help="Write Markdown report to this path (default: stdout only)",
|
|
)
|
|
args = parser.parse_args(argv)
|
|
|
|
report = generate_dashboard(args.data_dir, args.period)
|
|
if args.output:
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_text(report, encoding="utf-8")
|
|
print(f"Wrote {args.output}")
|
|
else:
|
|
print(report)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main()) |