Skip to main content

systemprompt_analytics/models/
engagement.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use systemprompt_identifiers::{ContentId, EngagementEventId, SessionId, UserId};
5
6#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
7pub struct EngagementEvent {
8    pub id: EngagementEventId,
9    pub session_id: SessionId,
10    pub user_id: UserId,
11    pub page_url: String,
12    pub content_id: Option<ContentId>,
13    pub event_type: String,
14    pub time_on_page_ms: i32,
15    pub time_to_first_interaction_ms: Option<i32>,
16    pub time_to_first_scroll_ms: Option<i32>,
17    pub max_scroll_depth: i32,
18    pub scroll_velocity_avg: Option<f32>,
19    pub scroll_direction_changes: Option<i32>,
20    pub click_count: i32,
21    pub mouse_move_distance_px: Option<i32>,
22    pub keyboard_events: Option<i32>,
23    pub copy_events: Option<i32>,
24    pub focus_time_ms: i32,
25    pub blur_count: i32,
26    pub tab_switches: i32,
27    pub visible_time_ms: i32,
28    pub hidden_time_ms: i32,
29    pub is_rage_click: Option<bool>,
30    pub is_dead_click: Option<bool>,
31    pub reading_pattern: Option<String>,
32    pub created_at: DateTime<Utc>,
33    pub updated_at: DateTime<Utc>,
34}
35
36#[derive(Debug, Clone, Deserialize)]
37#[serde(default)]
38pub struct CreateEngagementEventInput {
39    #[serde(default)]
40    pub page_url: String,
41    #[serde(default = "default_event_type")]
42    pub event_type: String,
43    #[serde(default)]
44    pub time_on_page_ms: i32,
45    #[serde(default)]
46    pub max_scroll_depth: i32,
47    #[serde(default)]
48    pub click_count: i32,
49    #[serde(default)]
50    pub event_data: Option<serde_json::Value>,
51    #[serde(flatten)]
52    pub optional_metrics: EngagementOptionalMetrics,
53}
54
55impl Default for CreateEngagementEventInput {
56    fn default() -> Self {
57        Self {
58            page_url: String::new(),
59            event_type: default_event_type(),
60            time_on_page_ms: 0,
61            max_scroll_depth: 0,
62            click_count: 0,
63            event_data: None,
64            optional_metrics: EngagementOptionalMetrics::default(),
65        }
66    }
67}
68
69#[derive(Debug, Clone, Default, Deserialize)]
70pub struct EngagementOptionalMetrics {
71    pub time_to_first_interaction_ms: Option<i32>,
72    pub time_to_first_scroll_ms: Option<i32>,
73    pub scroll_velocity_avg: Option<f32>,
74    pub scroll_direction_changes: Option<i32>,
75    pub mouse_move_distance_px: Option<i32>,
76    pub keyboard_events: Option<i32>,
77    pub copy_events: Option<i32>,
78    pub focus_time_ms: Option<i32>,
79    pub blur_count: Option<i32>,
80    pub tab_switches: Option<i32>,
81    pub visible_time_ms: Option<i32>,
82    pub hidden_time_ms: Option<i32>,
83    pub is_rage_click: Option<bool>,
84    pub is_dead_click: Option<bool>,
85    pub reading_pattern: Option<String>,
86}
87
88fn default_event_type() -> String {
89    "page_exit".to_string()
90}