systemprompt_cli/commands/core/content/analytics/
clicks.rs1use crate::cli_settings::CliConfig;
7use crate::commands::core::content::types::{ClickRow, ClicksOutput};
8use crate::context::CommandContext;
9use crate::shared::CommandOutput;
10use anyhow::Result;
11use clap::Args;
12use systemprompt_content::LinkAnalyticsService;
13use systemprompt_database::DbPool;
14use systemprompt_identifiers::LinkId;
15
16#[derive(Debug, Args)]
17pub struct ClicksArgs {
18 #[arg(help = "Link ID")]
19 pub link_id: String,
20
21 #[arg(long, default_value = "20")]
22 pub limit: i64,
23
24 #[arg(long, default_value = "0")]
25 pub offset: i64,
26}
27
28pub async fn execute(args: ClicksArgs, ctx: &CommandContext) -> Result<CommandOutput> {
29 execute_with_pool(args, &ctx.db_pool().await?, &ctx.cli).await
30}
31
32pub async fn execute_with_pool(
33 args: ClicksArgs,
34 pool: &DbPool,
35 _config: &CliConfig,
36) -> Result<CommandOutput> {
37 let service = LinkAnalyticsService::new(pool)?;
38
39 let link_id = LinkId::new(args.link_id.clone());
40 let clicks = service
41 .get_link_clicks(&link_id, Some(args.limit), Some(args.offset))
42 .await?;
43
44 let total = clicks.len() as i64;
45 let click_rows: Vec<ClickRow> = clicks
46 .into_iter()
47 .filter_map(|click| {
48 Some(ClickRow {
49 click_id: click.id.to_string(),
50 session_id: click.session_id,
51 user_id: click.user_id,
52 clicked_at: click.clicked_at?,
53 referrer_page: click.referrer_page,
54 device_type: click.device_type,
55 country: click.country,
56 is_conversion: click.is_conversion.unwrap_or(false),
57 })
58 })
59 .collect();
60
61 let output = ClicksOutput {
62 link_id,
63 clicks: click_rows,
64 total,
65 };
66
67 Ok(CommandOutput::table_of(
68 vec![
69 "click_id",
70 "session_id",
71 "clicked_at",
72 "device_type",
73 "country",
74 ],
75 &output.clicks,
76 )
77 .with_title("Link Clicks"))
78}