Skip to main content

systemprompt_models/events/
analytics_event.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use systemprompt_identifiers::SessionId;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")]
7pub enum AnalyticsEvent {
8    SessionStarted {
9        timestamp: DateTime<Utc>,
10        #[serde(flatten)]
11        payload: SessionStartedPayload,
12    },
13    SessionEnded {
14        timestamp: DateTime<Utc>,
15        #[serde(flatten)]
16        payload: SessionEndedPayload,
17    },
18    PageView {
19        timestamp: DateTime<Utc>,
20        #[serde(flatten)]
21        payload: PageViewPayload,
22    },
23    EngagementUpdate {
24        timestamp: DateTime<Utc>,
25        #[serde(flatten)]
26        payload: EngagementUpdatePayload,
27    },
28    RealTimeStats {
29        timestamp: DateTime<Utc>,
30        #[serde(flatten)]
31        payload: RealTimeStatsPayload,
32    },
33    Heartbeat {
34        timestamp: DateTime<Utc>,
35    },
36}
37
38impl AnalyticsEvent {
39    pub const fn timestamp(&self) -> DateTime<Utc> {
40        match self {
41            Self::SessionStarted { timestamp, .. }
42            | Self::SessionEnded { timestamp, .. }
43            | Self::PageView { timestamp, .. }
44            | Self::EngagementUpdate { timestamp, .. }
45            | Self::RealTimeStats { timestamp, .. }
46            | Self::Heartbeat { timestamp } => *timestamp,
47        }
48    }
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct SessionStartedPayload {
53    pub session_id: SessionId,
54    pub device_type: Option<String>,
55    pub browser: Option<String>,
56    pub os: Option<String>,
57    pub country: Option<String>,
58    pub referrer_source: Option<String>,
59    pub is_bot: bool,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct SessionEndedPayload {
64    pub session_id: SessionId,
65    pub duration_ms: i64,
66    pub page_count: i64,
67    pub request_count: i64,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct PageViewPayload {
72    pub session_id: SessionId,
73    pub user_id: Option<String>,
74    pub page_url: String,
75    pub content_id: Option<String>,
76    pub referrer: Option<String>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct EngagementUpdatePayload {
81    pub session_id: SessionId,
82    pub page_url: String,
83    pub scroll_depth: i32,
84    pub time_on_page_ms: i64,
85    pub click_count: i32,
86}
87
88#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
89pub struct RealTimeStatsPayload {
90    pub active_sessions: i64,
91    pub active_users: i64,
92    pub requests_per_minute: i64,
93    pub page_views_last_5m: i64,
94    pub bot_requests_last_5m: i64,
95}
96
97#[derive(Debug, Clone, Copy)]
98pub struct AnalyticsEventBuilder;
99
100impl AnalyticsEventBuilder {
101    pub fn session_started(payload: SessionStartedPayload) -> AnalyticsEvent {
102        AnalyticsEvent::SessionStarted {
103            timestamp: Utc::now(),
104            payload,
105        }
106    }
107
108    pub fn session_ended(
109        session_id: SessionId,
110        duration_ms: i64,
111        page_count: i64,
112        request_count: i64,
113    ) -> AnalyticsEvent {
114        AnalyticsEvent::SessionEnded {
115            timestamp: Utc::now(),
116            payload: SessionEndedPayload {
117                session_id,
118                duration_ms,
119                page_count,
120                request_count,
121            },
122        }
123    }
124
125    pub fn page_view(
126        session_id: SessionId,
127        user_id: Option<String>,
128        page_url: String,
129        content_id: Option<String>,
130        referrer: Option<String>,
131    ) -> AnalyticsEvent {
132        AnalyticsEvent::PageView {
133            timestamp: Utc::now(),
134            payload: PageViewPayload {
135                session_id,
136                user_id,
137                page_url,
138                content_id,
139                referrer,
140            },
141        }
142    }
143
144    pub fn engagement_update(
145        session_id: SessionId,
146        page_url: String,
147        scroll_depth: i32,
148        time_on_page_ms: i64,
149        click_count: i32,
150    ) -> AnalyticsEvent {
151        AnalyticsEvent::EngagementUpdate {
152            timestamp: Utc::now(),
153            payload: EngagementUpdatePayload {
154                session_id,
155                page_url,
156                scroll_depth,
157                time_on_page_ms,
158                click_count,
159            },
160        }
161    }
162
163    pub fn realtime_stats(
164        active_sessions: i64,
165        active_users: i64,
166        requests_per_minute: i64,
167        page_views_last_5m: i64,
168        bot_requests_last_5m: i64,
169    ) -> AnalyticsEvent {
170        AnalyticsEvent::RealTimeStats {
171            timestamp: Utc::now(),
172            payload: RealTimeStatsPayload {
173                active_sessions,
174                active_users,
175                requests_per_minute,
176                page_views_last_5m,
177                bot_requests_last_5m,
178            },
179        }
180    }
181
182    pub fn heartbeat() -> AnalyticsEvent {
183        AnalyticsEvent::Heartbeat {
184            timestamp: Utc::now(),
185        }
186    }
187}