Implement client cost attribution
This commit is contained in:
parent
d2b9bc4b32
commit
33883e0977
15 changed files with 362 additions and 21 deletions
|
|
@ -6,6 +6,8 @@ from collections import defaultdict
|
|||
from dataclasses import dataclass
|
||||
from typing import Iterable
|
||||
|
||||
from fin_hub.attribution import optional_attribution
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ServiceCostLine:
|
||||
|
|
@ -15,6 +17,19 @@ class ServiceCostLine:
|
|||
amount: float
|
||||
currency: str
|
||||
source: str
|
||||
client_id: str | None = None
|
||||
application_id: str | None = None
|
||||
app_instance_id: str | None = None
|
||||
cost_attribution_key: str | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
attribution = optional_attribution(
|
||||
self.client_id, self.application_id, self.app_instance_id
|
||||
)
|
||||
expected_key = attribution.key if attribution else None
|
||||
if self.cost_attribution_key not in (None, expected_key):
|
||||
raise ValueError("cost_attribution_key does not match its attribution dimensions")
|
||||
object.__setattr__(self, "cost_attribution_key", expected_key)
|
||||
|
||||
|
||||
def build_service_cost_report(
|
||||
|
|
@ -22,6 +37,7 @@ def build_service_cost_report(
|
|||
) -> dict:
|
||||
by_service: dict[str, dict] = {}
|
||||
totals_by_month: dict[str, float] = defaultdict(float)
|
||||
by_attribution: dict[tuple[str, str], dict] = {}
|
||||
for line in lines:
|
||||
bucket = by_service.setdefault(
|
||||
line.service_id,
|
||||
|
|
@ -37,11 +53,31 @@ def build_service_cost_report(
|
|||
bucket["months"][line.period_month] = month_total
|
||||
bucket["total"] += line.amount
|
||||
totals_by_month[line.period_month] += line.amount
|
||||
key = (line.cost_attribution_key or "unattributed", line.currency)
|
||||
attribution = by_attribution.setdefault(
|
||||
key,
|
||||
{
|
||||
"cost_attribution_key": line.cost_attribution_key,
|
||||
"client_id": line.client_id,
|
||||
"application_id": line.application_id,
|
||||
"app_instance_id": line.app_instance_id,
|
||||
"currency": line.currency,
|
||||
"months": {},
|
||||
"total": 0.0,
|
||||
},
|
||||
)
|
||||
attribution["months"][line.period_month] = (
|
||||
attribution["months"].get(line.period_month, 0.0) + line.amount
|
||||
)
|
||||
attribution["total"] += line.amount
|
||||
services = sorted(by_service.values(), key=lambda item: item["total"], reverse=True)
|
||||
return {
|
||||
"source_hub": "fin-hub",
|
||||
"target_hub": "ops-hub",
|
||||
"signal": "service_cost_attribution",
|
||||
"services": services,
|
||||
"attributions": sorted(
|
||||
by_attribution.values(), key=lambda item: item["total"], reverse=True
|
||||
),
|
||||
"totals_by_month": dict(sorted(totals_by_month.items())),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue