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