Skip to main content

systemprompt_cli/commands/analytics/content/
mod.rs

1//! Content performance analytics: engagement stats, top content, and trends.
2//!
3//! Defines the [`ContentCommands`] subcommand tree and the typed output shapes
4//! ([`ContentStatsOutput`], [`TopContentOutput`], [`ContentTrendsOutput`])
5//! rendered by the `analytics content` commands.
6//!
7//! Copyright (c) systemprompt.io — Business Source License 1.1.
8//! See <https://systemprompt.io> for licensing details.
9
10mod stats;
11mod top;
12mod trends;
13
14use anyhow::Result;
15use clap::Subcommand;
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18
19use crate::context::CommandContext;
20use crate::shared::render_result;
21
22#[derive(Debug, Subcommand)]
23pub enum ContentCommands {
24    #[command(about = "Content engagement statistics", alias = "list")]
25    Stats(stats::StatsArgs),
26
27    #[command(about = "Top performing content")]
28    Top(top::TopArgs),
29
30    #[command(about = "Top performing content", hide = true)]
31    Popular(top::TopArgs),
32
33    #[command(about = "Content trends over time")]
34    Trends(trends::TrendsArgs),
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
38pub struct ContentStatsOutput {
39    pub period: String,
40    pub total_views: i64,
41    pub unique_visitors: i64,
42    pub avg_time_on_page_seconds: i64,
43    pub avg_scroll_depth: f64,
44    pub total_clicks: i64,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
48pub struct TopContentRow {
49    #[serde(rename = "content_id")]
50    pub content: String,
51    pub slug: String,
52    pub title: String,
53    pub source: String,
54    pub views: i64,
55    pub unique_visitors: i64,
56    pub avg_time_seconds: i64,
57    pub trend: String,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
61pub struct TopContentOutput {
62    pub period: String,
63    pub content: Vec<TopContentRow>,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
67pub struct ContentTrendPoint {
68    pub timestamp: String,
69    pub views: i64,
70    pub unique_visitors: i64,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
74pub struct ContentTrendsOutput {
75    pub period: String,
76    pub group_by: String,
77    pub points: Vec<ContentTrendPoint>,
78}
79
80pub async fn execute(command: ContentCommands, ctx: &CommandContext) -> Result<()> {
81    let db_ctx = ctx.database().await?;
82    match command {
83        ContentCommands::Stats(args) => {
84            let result = stats::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
85            render_result(&result, &ctx.cli);
86            Ok(())
87        },
88        ContentCommands::Top(args) | ContentCommands::Popular(args) => {
89            let result = top::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
90            render_result(&result, &ctx.cli);
91            Ok(())
92        },
93        ContentCommands::Trends(args) => {
94            let result = trends::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
95            render_result(&result, &ctx.cli);
96            Ok(())
97        },
98    }
99}