use crate::models::AccountingNetwork;
use std::collections::HashMap;
pub struct AnalyticsEngine {
pub thresholds: RiskThresholds,
}
#[derive(Debug, Clone)]
pub struct RiskThresholds {
pub suspense_threshold: f32,
pub confidence_threshold: f32,
pub benford_chi_sq_threshold: f64,
pub z_score_threshold: f64,
}
impl Default for RiskThresholds {
fn default() -> Self {
Self {
suspense_threshold: 0.7,
confidence_threshold: 0.5,
benford_chi_sq_threshold: 15.507,
z_score_threshold: 3.0,
}
}
}
impl AnalyticsEngine {
pub fn new() -> Self {
Self {
thresholds: RiskThresholds::default(),
}
}
pub fn with_thresholds(thresholds: RiskThresholds) -> Self {
Self { thresholds }
}
pub fn analyze(&self, network: &AccountingNetwork) -> AnalyticsSnapshot {
let mut snapshot = AnalyticsSnapshot {
overall_risk: self.calculate_overall_risk(network),
suspense_accounts: network.statistics.suspense_account_count,
gaap_violations: network.statistics.gaap_violation_count,
fraud_patterns: network.statistics.fraud_pattern_count,
..Default::default()
};
for account in &network.accounts {
snapshot.account_risks.insert(
account.index,
RiskScore {
total: account.risk_score,
suspense_component: account.suspense_score * 0.3,
fraud_component: if account
.flags
.has(crate::models::AccountFlags::HAS_FRAUD_PATTERN)
{
0.5
} else {
0.0
},
confidence_component: 0.0, },
);
}
snapshot.network_health = NetworkHealth {
balance_check: true, coverage: network.statistics.avg_confidence,
connectivity: network.statistics.density,
};
snapshot
}
fn calculate_overall_risk(&self, network: &AccountingNetwork) -> f32 {
let n = network.accounts.len().max(1) as f32;
let suspense_ratio = network.statistics.suspense_account_count as f32 / n;
let violation_ratio = network.statistics.gaap_violation_count as f32 / n;
let fraud_ratio = network.statistics.fraud_pattern_count as f32 / n;
let confidence_factor = 1.0 - network.statistics.avg_confidence as f32;
(0.25 * suspense_ratio
+ 0.30 * violation_ratio
+ 0.35 * fraud_ratio
+ 0.10 * confidence_factor)
.min(1.0)
}
}
impl Default for AnalyticsEngine {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Default)]
pub struct AnalyticsSnapshot {
pub overall_risk: f32,
pub suspense_accounts: usize,
pub gaap_violations: usize,
pub fraud_patterns: usize,
pub account_risks: HashMap<u16, RiskScore>,
pub network_health: NetworkHealth,
}
#[derive(Debug, Clone, Default)]
pub struct RiskScore {
pub total: f32,
pub suspense_component: f32,
pub fraud_component: f32,
pub confidence_component: f32,
}
#[derive(Debug, Clone, Default)]
pub struct NetworkHealth {
pub balance_check: bool,
pub coverage: f64,
pub connectivity: f64,
}
#[cfg(test)]
mod tests {
use super::*;
use uuid::Uuid;
#[test]
fn test_analytics_engine() {
let engine = AnalyticsEngine::new();
let network = AccountingNetwork::new(Uuid::new_v4(), 2024, 1);
let snapshot = engine.analyze(&network);
assert!(snapshot.overall_risk >= 0.0 && snapshot.overall_risk <= 1.0);
}
}