ccboard_core/models/
invocations.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Default, Serialize, Deserialize)]
9pub struct InvocationStats {
10 pub agents: HashMap<String, usize>,
13
14 pub commands: HashMap<String, usize>,
17
18 pub skills: HashMap<String, usize>,
21
22 #[serde(default)]
25 pub agent_token_stats: HashMap<String, u64>,
26
27 pub last_computed: DateTime<Utc>,
29
30 pub sessions_analyzed: usize,
32}
33
34impl InvocationStats {
35 pub fn new() -> Self {
37 Self {
38 agents: HashMap::new(),
39 commands: HashMap::new(),
40 skills: HashMap::new(),
41 agent_token_stats: HashMap::new(),
42 last_computed: Utc::now(),
43 sessions_analyzed: 0,
44 }
45 }
46
47 pub fn total_invocations(&self) -> usize {
49 self.agents.values().sum::<usize>()
50 + self.commands.values().sum::<usize>()
51 + self.skills.values().sum::<usize>()
52 }
53
54 pub fn merge(&mut self, other: &InvocationStats) {
56 for (name, count) in &other.agents {
57 *self.agents.entry(name.clone()).or_insert(0) += count;
58 }
59 for (name, count) in &other.commands {
60 *self.commands.entry(name.clone()).or_insert(0) += count;
61 }
62 for (name, count) in &other.skills {
63 *self.skills.entry(name.clone()).or_insert(0) += count;
64 }
65 for (name, tokens) in &other.agent_token_stats {
66 *self.agent_token_stats.entry(name.clone()).or_insert(0) += tokens;
67 }
68 self.sessions_analyzed += other.sessions_analyzed;
69 if other.last_computed > self.last_computed {
71 self.last_computed = other.last_computed;
72 }
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_total_invocations() {
82 let mut stats = InvocationStats::new();
83 stats.agents.insert("technical-writer".to_string(), 5);
84 stats.commands.insert("/commit".to_string(), 12);
85 stats.skills.insert("pdf-generator".to_string(), 3);
86
87 assert_eq!(stats.total_invocations(), 20);
88 }
89
90 #[test]
91 fn test_merge() {
92 let mut stats1 = InvocationStats::new();
93 stats1.agents.insert("debugger".to_string(), 3);
94 stats1.commands.insert("/commit".to_string(), 5);
95 stats1.sessions_analyzed = 10;
96
97 let mut stats2 = InvocationStats::new();
98 stats2.agents.insert("debugger".to_string(), 2);
99 stats2.agents.insert("code-reviewer".to_string(), 1);
100 stats2.commands.insert("/commit".to_string(), 7);
101 stats2.sessions_analyzed = 5;
102
103 stats1.merge(&stats2);
104
105 assert_eq!(stats1.agents.get("debugger"), Some(&5));
106 assert_eq!(stats1.agents.get("code-reviewer"), Some(&1));
107 assert_eq!(stats1.commands.get("/commit"), Some(&12));
108 assert_eq!(stats1.sessions_analyzed, 15);
109 }
110}