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