Skip to main content

systemprompt_cli/commands/core/content/analytics/
clicks.rs

1//! `core content analytics clicks` command.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use 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::{ContentRepositories, 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 repositories = ContentRepositories::new(pool)?;
38    let service = LinkAnalyticsService::new(repositories.link, repositories.link_analytics);
39
40    let link_id = LinkId::new(args.link_id.clone());
41    let clicks = service
42        .get_link_clicks(&link_id, Some(args.limit), Some(args.offset))
43        .await?;
44
45    let total = clicks.len() as i64;
46    let click_rows: Vec<ClickRow> = clicks
47        .into_iter()
48        .filter_map(|click| {
49            Some(ClickRow {
50                click_id: click.id.to_string(),
51                session_id: click.session_id,
52                user_id: click.user_id,
53                clicked_at: click.clicked_at?,
54                referrer_page: click.referrer_page,
55                device_type: click.device_type,
56                country: click.country,
57                is_conversion: click.is_conversion.unwrap_or(false),
58            })
59        })
60        .collect();
61
62    let output = ClicksOutput {
63        link_id,
64        clicks: click_rows,
65        total,
66    };
67
68    Ok(CommandOutput::table_of(
69        vec![
70            "click_id",
71            "session_id",
72            "clicked_at",
73            "device_type",
74            "country",
75        ],
76        &output.clicks,
77    )
78    .with_title("Link Clicks"))
79}