difflore_core/skills/
stats.rs1use super::REMEMBER_DAILY_LIMIT;
2
3#[derive(Debug, Clone, serde::Serialize)]
4#[serde(rename_all = "camelCase")]
5pub struct RulesStats {
6 pub total: i64,
7 pub by_origin: Vec<OriginCount>,
8 pub conversation_captures_today: i64,
9 pub conversation_daily_limit: i64,
10 pub top_strengthened: Vec<StrengthenedRule>,
11}
12
13#[derive(Debug, Clone, serde::Serialize)]
14#[serde(rename_all = "camelCase")]
15pub struct OriginCount {
16 pub origin: String,
17 pub count: i64,
18}
19
20#[derive(Debug, Clone, serde::Serialize)]
21#[serde(rename_all = "camelCase")]
22pub struct StrengthenedRule {
23 pub id: String,
24 pub name: String,
25 pub origin: String,
26 pub confidence: f64,
27}
28
29pub async fn stats(db: &sqlx::SqlitePool) -> crate::Result<RulesStats> {
30 let total = sqlx::query_scalar!("SELECT COUNT(*) FROM skills WHERE status = 'active'")
31 .fetch_one(db)
32 .await?;
33
34 let by_origin_rows = sqlx::query!(
38 "SELECT origin, COUNT(*) AS c FROM skills WHERE status = 'active' \
39 GROUP BY origin ORDER BY c DESC, origin ASC",
40 )
41 .fetch_all(db)
42 .await?;
43 let by_origin: Vec<OriginCount> = by_origin_rows
44 .into_iter()
45 .map(|r| OriginCount {
46 origin: r.origin,
47 count: r.c,
48 })
49 .collect();
50
51 let conversation_captures_today = count_captures_today(db, "conversation").await?;
52
53 let top_rows = sqlx::query!(
58 "SELECT id, name, origin, confidence_score FROM skills \
59 WHERE origin = 'conversation' AND confidence_score > 0.6 \
60 AND status = 'active' \
61 ORDER BY confidence_score DESC, updated_at DESC LIMIT 5",
62 )
63 .fetch_all(db)
64 .await?;
65 let top_strengthened: Vec<StrengthenedRule> = top_rows
66 .into_iter()
67 .map(|r| StrengthenedRule {
68 id: r.id,
69 name: r.name,
70 origin: r.origin,
71 confidence: r.confidence_score,
72 })
73 .collect();
74
75 Ok(RulesStats {
76 total,
77 by_origin,
78 conversation_captures_today,
79 conversation_daily_limit: REMEMBER_DAILY_LIMIT,
80 top_strengthened,
81 })
82}
83
84pub async fn count_captures_today(db: &sqlx::SqlitePool, origin: &str) -> crate::Result<i64> {
90 if origin != "conversation" {
91 return Ok(0);
92 }
93 let local_day = chrono::Local::now().date_naive().to_string();
94 let n = sqlx::query_scalar::<_, i64>(
95 "SELECT
96 (SELECT COUNT(*) FROM skills
97 WHERE origin = 'conversation'
98 AND date(installed_at, 'localtime') = ?1)
99 +
100 (SELECT COUNT(*) FROM rule_events
101 WHERE source = 'remember_rule'
102 AND date(created_at, 'localtime') = ?1)",
103 )
104 .bind(local_day)
105 .fetch_one(db)
106 .await?;
107 Ok(n)
108}