///|
pub struct DashboardCard {
key : String
title : String
value : Double
target : Double
unit : String
trend : Int
status : String
} derive(Debug, ToJson)
///|
pub struct DashboardSnapshot {
name : String
generated_at : String
cards : Array[DashboardCard]
mut alerts : Int
mut score : Double
} derive(Debug, ToJson)
///|
pub fn dashboard_card(
key~ : String,
title~ : String,
value~ : Double,
target~ : Double,
unit~ : String,
trend~ : Int,
status~ : String,
) -> DashboardCard {
{ key, title, value, target, unit, trend, status }
}
///|
pub fn dashboard_snapshot(
name~ : String,
generated_at~ : String,
) -> DashboardSnapshot {
{ name, generated_at, cards: [], alerts: 0, score: 0.0 }
}
///|
pub fn DashboardSnapshot::add_card(
self : DashboardSnapshot,
card : DashboardCard,
) -> Unit {
self.cards.push(card)
}
///|
pub fn DashboardSnapshot::set_alerts(
self : DashboardSnapshot,
alerts : Int,
) -> Unit {
self.alerts = alerts.max(0)
}
///|
pub fn DashboardSnapshot::set_score(
self : DashboardSnapshot,
score : Double,
) -> Unit {
self.score = score.max(0.0).min(100.0)
}
///|
pub fn card_gap(card : DashboardCard) -> Double {
(card.value - card.target).abs()
}
///|
pub fn card_meets_target(card : DashboardCard) -> Bool {
card.status == "good" || card_gap(card) <= 0.0
}
///|
pub fn card_status_from_gap(
gap : Double,
warning : Double,
critical : Double,
) -> String {
if gap >= critical {
"critical"
} else if gap >= warning {
"warning"
} else {
"good"
}
}
///|
pub fn snapshot_card_count(snapshot : DashboardSnapshot) -> Int {
snapshot.cards.length()
}
///|
pub fn snapshot_failed_cards(snapshot : DashboardSnapshot) -> Int {
let mut count = 0
for card in snapshot.cards {
if !card_meets_target(card) {
count += 1
}
}
count
}
///|
pub fn snapshot_good_cards(snapshot : DashboardSnapshot) -> Int {
snapshot.cards.length() - snapshot_failed_cards(snapshot)
}
///|
pub fn snapshot_completion(snapshot : DashboardSnapshot) -> Double {
if snapshot.cards.length() == 0 {
0.0
} else {
snapshot_good_cards(snapshot).to_double() /
snapshot.cards.length().to_double()
}
}
///|
pub fn snapshot_card(
snapshot : DashboardSnapshot,
key : String,
) -> DashboardCard? {
for card in snapshot.cards {
if card.key == key {
return Some(card)
}
}
None
}
///|
pub fn snapshot_keys(snapshot : DashboardSnapshot) -> Array[String] {
let result : Array[String] = []
for card in snapshot.cards {
result.push(card.key)
}
result
}
///|
pub fn snapshot_units(snapshot : DashboardSnapshot) -> Array[String] {
let result : Array[String] = []
for card in snapshot.cards {
if !result.contains(card.unit) {
result.push(card.unit)
}
}
result
}
///|
pub fn snapshot_trending_up(snapshot : DashboardSnapshot) -> Int {
let mut count = 0
for card in snapshot.cards {
if card.trend > 0 {
count += 1
}
}
count
}
///|
pub fn snapshot_trending_down(snapshot : DashboardSnapshot) -> Int {
let mut count = 0
for card in snapshot.cards {
if card.trend < 0 {
count += 1
}
}
count
}
///|
pub fn snapshot_critical_count(snapshot : DashboardSnapshot) -> Int {
let mut count = 0
for card in snapshot.cards {
if card.status == "critical" {
count += 1
}
}
count
}
///|
pub fn snapshot_warning_count(snapshot : DashboardSnapshot) -> Int {
let mut count = 0
for card in snapshot.cards {
if card.status == "warning" {
count += 1
}
}
count
}
///|
pub fn snapshot_health(snapshot : DashboardSnapshot) -> String {
if snapshot_critical_count(snapshot) > 0 || snapshot.alerts > 0 {
"critical"
} else if snapshot_warning_count(snapshot) > 0 {
"warning"
} else {
"healthy"
}
}
///|
pub fn sort_cards_by_gap(snapshot : DashboardSnapshot) -> Array[DashboardCard] {
let result = snapshot.cards.copy()
result.sort_by(fn(a, b) { if card_gap(a) > card_gap(b) { -1 } else { 1 } })
result
}
///|
pub fn card_trend_name(trend : Int) -> String {
if trend > 0 {
"up"
} else if trend < 0 {
"down"
} else {
"flat"
}
}
///|
pub fn snapshot_target_rate(snapshot : DashboardSnapshot) -> Double {
if snapshot.cards.length() == 0 {
0.0
} else {
let mut total = 0.0
for card in snapshot.cards {
if card.target != 0.0 {
total += card.value / card.target
}
}
total / snapshot.cards.length().to_double()
}
}
///|
pub fn snapshot_alert_density(snapshot : DashboardSnapshot) -> Double {
if snapshot.cards.length() == 0 {
0.0
} else {
snapshot.alerts.to_double() / snapshot.cards.length().to_double()
}
}
///|
pub fn snapshot_card_values(snapshot : DashboardSnapshot) -> Array[Double] {
let result : Array[Double] = []
for card in snapshot.cards {
result.push(card.value)
}
result
}
///|
pub fn snapshot_card_targets(snapshot : DashboardSnapshot) -> Array[Double] {
let result : Array[Double] = []
for card in snapshot.cards {
result.push(card.target)
}
result
}
///|
pub fn snapshot_average_gap(snapshot : DashboardSnapshot) -> Double {
if snapshot.cards.length() == 0 {
0.0
} else {
let mut total = 0.0
for card in snapshot.cards {
total += card_gap(card)
}
total / snapshot.cards.length().to_double()
}
}
///|
pub fn snapshot_max_gap(snapshot : DashboardSnapshot) -> Double {
let mut high = 0.0
for card in snapshot.cards {
high = high.max(card_gap(card))
}
high
}
///|
pub fn snapshot_has_key(snapshot : DashboardSnapshot, key : String) -> Bool {
snapshot_card(snapshot, key) is Some(_)
}
///|
pub fn snapshot_is_empty(snapshot : DashboardSnapshot) -> Bool {
snapshot.cards.length() == 0
}
///|
pub fn snapshot_quality_score(snapshot : DashboardSnapshot) -> Double {
snapshot_completion(snapshot) * 100.0 - snapshot.alerts.to_double().min(100.0)
}
///|
pub fn snapshot_summary_line(snapshot : DashboardSnapshot) -> String {
"name=\{snapshot.name}; cards=\{snapshot.cards.length()}; health=\{snapshot_health(snapshot)}; score=\{snapshot_quality_score(snapshot)}"
}
///|
pub fn snapshot_card_statuses(snapshot : DashboardSnapshot) -> Array[String] {
let result : Array[String] = []
for card in snapshot.cards {
result.push(card.status)
}
result
}
///|
pub fn snapshot_filter_status(
snapshot : DashboardSnapshot,
status : String,
) -> Array[DashboardCard] {
let result : Array[DashboardCard] = []
for card in snapshot.cards {
if card.status == status {
result.push(card)
}
}
result
}
///|
pub fn snapshot_filter_unit(
snapshot : DashboardSnapshot,
unit : String,
) -> Array[DashboardCard] {
let result : Array[DashboardCard] = []
for card in snapshot.cards {
if card.unit == unit {
result.push(card)
}
}
result
}
///|
pub fn snapshot_merge(
left : DashboardSnapshot,
right : DashboardSnapshot,
) -> DashboardSnapshot {
let result = dashboard_snapshot(
name=left.name + "+" + right.name,
generated_at=right.generated_at,
)
for card in left.cards {
result.add_card(card)
}
for card in right.cards {
result.add_card(card)
}
result.set_alerts(left.alerts + right.alerts)
result.set_score((left.score + right.score) / 2.0)
result
}