///|
/// Deterministic scalar metrics for dashboards and reports.
pub struct DimensionTotal {
dimension : String
amount : Amount
entries : Int
} derive(Debug, Eq)
///|
pub fn group_by_dimension(
entries : Array[Entry],
period : String,
) -> Array[DimensionTotal] {
let result : Array[DimensionTotal] = []
for entry in entries {
if entry.period != period {
continue
}
let key = if entry.dimension == "" { "default" } else { entry.dimension }
let amount = entry.credit - entry.debit
for i, item in result {
if item.dimension == key {
result[i] = {
..item,
amount: item.amount + amount,
entries: item.entries + 1,
}
continue
}
}
if find_dimension_total(result, key) is None {
result.push({ dimension: key, amount, entries: 1 })
}
}
result
}
///|
fn find_dimension_total(
items : Array[DimensionTotal],
dimension : String,
) -> DimensionTotal? {
for item in items {
if item.dimension == dimension {
return Some(item)
}
}
None
}
///|
pub fn growth_percent(previous : Amount, current : Amount) -> Amount? {
if previous == 0 {
None
} else {
Some((current - previous) * 100 / previous)
}
}
///|
pub fn weighted_average(values : Array[(Amount, Int)]) -> Amount? {
let mut total = 0
let mut weight = 0
for pair in values {
let value = pair.0
let factor = pair.1
if factor < 0 {
continue
}
total += value * factor
weight += factor
}
if weight == 0 {
None
} else {
Some(total / weight)
}
}
///|
pub fn net_margin(revenue : Amount, expenses : Amount) -> Amount? {
if revenue == 0 {
None
} else {
Some((revenue - expenses) * 100 / revenue)
}
}
///|
pub fn burn_rate(expenses : Array[Amount], periods : Int) -> Amount? {
if periods <= 0 {
None
} else {
let mut total = 0
for expense in expenses {
total += expense
}
Some(total / periods)
}
}