systemprompt_cli/commands/analytics/traffic/
mod.rs1mod bots;
11mod devices;
12mod geo;
13mod navigation;
14mod pages;
15mod sources;
16
17use anyhow::Result;
18use clap::Subcommand;
19use schemars::JsonSchema;
20use serde::{Deserialize, Serialize};
21
22use crate::context::CommandContext;
23use crate::shared::render_result;
24
25#[derive(Debug, Subcommand)]
26pub enum TrafficCommands {
27 #[command(about = "Traffic source breakdown", alias = "list")]
28 Sources(sources::SourcesArgs),
29
30 #[command(about = "Sessions by landing page")]
31 Pages(pages::PagesArgs),
32
33 #[command(about = "Internal link-click transitions")]
34 Navigation(navigation::NavigationArgs),
35
36 #[command(about = "Geographic distribution")]
37 Geo(geo::GeoArgs),
38
39 #[command(about = "Device and browser breakdown")]
40 Devices(devices::DevicesArgs),
41
42 #[command(about = "Bot traffic analysis")]
43 Bots(bots::BotsArgs),
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
47pub struct TrafficSourceRow {
48 pub source: String,
49 pub session_count: i64,
50 pub percentage: f64,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
54pub struct TrafficSourcesOutput {
55 pub period: String,
56 pub sources: Vec<TrafficSourceRow>,
57 pub total_sessions: i64,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
61pub struct TrafficPageRow {
62 pub page: String,
63 pub source: String,
64 pub session_count: i64,
65 pub percentage: f64,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
69pub struct TrafficPagesOutput {
70 pub period: String,
71 pub pages: Vec<TrafficPageRow>,
72 pub total_sessions: i64,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
76pub struct NavigationRow {
77 pub from_path: String,
78 pub to_path: String,
79 pub click_count: i64,
80 pub percentage: f64,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
84pub struct NavigationOutput {
85 pub period: String,
86 pub transitions: Vec<NavigationRow>,
87 pub total_clicks: i64,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
91pub struct GeoRow {
92 pub country: String,
93 pub session_count: i64,
94 pub percentage: f64,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
98pub struct GeoOutput {
99 pub period: String,
100 pub countries: Vec<GeoRow>,
101 pub total_sessions: i64,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
105pub struct DeviceRow {
106 pub device_type: String,
107 pub browser: String,
108 pub session_count: i64,
109 pub percentage: f64,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
113pub struct DevicesOutput {
114 pub period: String,
115 pub devices: Vec<DeviceRow>,
116 pub total_sessions: i64,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
120pub struct BotRow {
121 pub bot_type: String,
122 pub session_count: i64,
123 pub percentage: f64,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
127pub struct BotsOutput {
128 pub period: String,
129 pub human_sessions: i64,
130 pub ghost_sessions: i64,
131 pub bot_sessions: i64,
132 pub bot_percentage: f64,
133 pub bot_breakdown: Vec<BotRow>,
134}
135
136pub async fn execute(command: TrafficCommands, ctx: &CommandContext) -> Result<()> {
137 let db_ctx = ctx.database().await?;
138 match command {
139 TrafficCommands::Sources(args) => {
140 let result = sources::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
141 render_result(&result, &ctx.cli);
142 Ok(())
143 },
144 TrafficCommands::Pages(args) => {
145 let result = pages::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
146 render_result(&result, &ctx.cli);
147 Ok(())
148 },
149 TrafficCommands::Navigation(args) => {
150 let result = navigation::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
151 render_result(&result, &ctx.cli);
152 Ok(())
153 },
154 TrafficCommands::Geo(args) => {
155 let result = geo::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
156 render_result(&result, &ctx.cli);
157 Ok(())
158 },
159 TrafficCommands::Devices(args) => {
160 let result = devices::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
161 render_result(&result, &ctx.cli);
162 Ok(())
163 },
164 TrafficCommands::Bots(args) => {
165 let result = bots::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
166 render_result(&result, &ctx.cli);
167 Ok(())
168 },
169 }
170}