systemprompt_cli/commands/analytics/content/
mod.rs1mod stats;
2mod top;
3mod trends;
4
5use anyhow::Result;
6use clap::Subcommand;
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9use systemprompt_runtime::DatabaseContext;
10
11use crate::CliConfig;
12use crate::shared::render_result;
13
14#[derive(Debug, Subcommand)]
15pub enum ContentCommands {
16 #[command(about = "Content engagement statistics")]
17 Stats(stats::StatsArgs),
18
19 #[command(about = "Top performing content")]
20 Top(top::TopArgs),
21
22 #[command(about = "Top performing content", hide = true)]
23 Popular(top::TopArgs),
24
25 #[command(about = "Content trends over time")]
26 Trends(trends::TrendsArgs),
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
30pub struct ContentStatsOutput {
31 pub period: String,
32 pub total_views: i64,
33 pub unique_visitors: i64,
34 pub avg_time_on_page_seconds: i64,
35 pub avg_scroll_depth: f64,
36 pub total_clicks: i64,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
40pub struct TopContentRow {
41 #[serde(rename = "content_id")]
42 pub content: String,
43 pub slug: String,
44 pub title: String,
45 pub source: String,
46 pub views: i64,
47 pub unique_visitors: i64,
48 pub avg_time_seconds: i64,
49 pub trend: String,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
53pub struct TopContentOutput {
54 pub period: String,
55 pub content: Vec<TopContentRow>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
59pub struct ContentTrendPoint {
60 pub timestamp: String,
61 pub views: i64,
62 pub unique_visitors: i64,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
66pub struct ContentTrendsOutput {
67 pub period: String,
68 pub group_by: String,
69 pub points: Vec<ContentTrendPoint>,
70}
71
72pub async fn execute(command: ContentCommands, config: &CliConfig) -> Result<()> {
73 match command {
74 ContentCommands::Stats(args) => {
75 let result = stats::execute(args, config).await?;
76 render_result(&result);
77 Ok(())
78 },
79 ContentCommands::Top(args) | ContentCommands::Popular(args) => {
80 let result = top::execute(args, config).await?;
81 render_result(&result);
82 Ok(())
83 },
84 ContentCommands::Trends(args) => {
85 let result = trends::execute(args, config).await?;
86 render_result(&result);
87 Ok(())
88 },
89 }
90}
91
92pub async fn execute_with_pool(
93 command: ContentCommands,
94 db_ctx: &DatabaseContext,
95 config: &CliConfig,
96) -> Result<()> {
97 match command {
98 ContentCommands::Stats(args) => {
99 let result = stats::execute_with_pool(args, db_ctx, config).await?;
100 render_result(&result);
101 Ok(())
102 },
103 ContentCommands::Top(args) | ContentCommands::Popular(args) => {
104 let result = top::execute_with_pool(args, db_ctx, config).await?;
105 render_result(&result);
106 Ok(())
107 },
108 ContentCommands::Trends(args) => {
109 let result = trends::execute_with_pool(args, db_ctx, config).await?;
110 render_result(&result);
111 Ok(())
112 },
113 }
114}