Skip to main content

kaizen/store/sqlite/
feedback.rs

1use super::rows::*;
2use super::*;
3
4impl Store {
5    pub fn upsert_feedback(&self, r: &crate::feedback::types::FeedbackRecord) -> Result<()> {
6        use crate::feedback::types::FeedbackLabel;
7        self.conn.execute(
8            "INSERT OR REPLACE INTO session_feedback
9             (id, session_id, score, label, note, created_at_ms)
10             VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
11            rusqlite::params![
12                r.id,
13                r.session_id,
14                r.score.as_ref().map(|s| s.0 as i64),
15                r.label.as_ref().map(FeedbackLabel::to_db_str),
16                r.note,
17                r.created_at_ms as i64,
18            ],
19        )?;
20        let payload = serde_json::to_string(r).unwrap_or_default();
21        self.conn.execute(
22            "INSERT INTO sync_outbox (session_id, kind, payload, sent)
23             VALUES (?1, 'session_feedback', ?2, 0)",
24            rusqlite::params![r.session_id, payload],
25        )?;
26        Ok(())
27    }
28
29    pub fn list_feedback_in_window(
30        &self,
31        start_ms: u64,
32        end_ms: u64,
33    ) -> Result<Vec<crate::feedback::types::FeedbackRecord>> {
34        let mut stmt = self.conn.prepare(
35            "SELECT id, session_id, score, label, note, created_at_ms
36             FROM session_feedback
37             WHERE created_at_ms >= ?1 AND created_at_ms < ?2
38             ORDER BY created_at_ms ASC",
39        )?;
40        let rows = stmt.query_map(
41            rusqlite::params![start_ms as i64, end_ms as i64],
42            feedback_row,
43        )?;
44        rows.map(|r| r.map_err(anyhow::Error::from)).collect()
45    }
46
47    pub fn feedback_for_sessions(
48        &self,
49        ids: &[String],
50    ) -> Result<std::collections::HashMap<String, crate::feedback::types::FeedbackRecord>> {
51        if ids.is_empty() {
52            return Ok(std::collections::HashMap::new());
53        }
54        let placeholders = ids.iter().map(|_| "?").collect::<Vec<_>>().join(",");
55        let sql = format!(
56            "SELECT id, session_id, score, label, note, created_at_ms
57             FROM session_feedback WHERE session_id IN ({placeholders})
58             ORDER BY created_at_ms DESC"
59        );
60        let mut stmt = self.conn.prepare(&sql)?;
61        let params: Vec<&dyn rusqlite::ToSql> =
62            ids.iter().map(|s| s as &dyn rusqlite::ToSql).collect();
63        let rows = stmt.query_map(params.as_slice(), feedback_row)?;
64        let mut map = std::collections::HashMap::new();
65        for row in rows {
66            let r = row?;
67            map.entry(r.session_id.clone()).or_insert(r);
68        }
69        Ok(map)
70    }
71}