Yimaru-BackEnd/gen/db/analytics.sql.go
Yared Yemane e957eacf80 Add profile field breakdowns to analytics dashboard.
Expose user counts by education_level, occupation, learning_goal, and language_challange on GET /api/v1/analytics/dashboard.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:54:47 -07:00

1472 lines
46 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: analytics.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const AnalyticsCourseCounts = `-- name: AnalyticsCourseCounts :one
SELECT
(SELECT COUNT(*)::bigint FROM programs) AS total_categories,
(SELECT COUNT(*)::bigint FROM courses) AS total_courses,
(SELECT COUNT(*)::bigint FROM modules) AS total_sub_courses,
(
COALESCE((SELECT COUNT(*)::bigint FROM lessons WHERE NULLIF(BTRIM(video_url), '') IS NOT NULL), 0::bigint)
+ COALESCE((SELECT COUNT(*)::bigint FROM exam_prep.unit_module_lessons WHERE NULLIF(BTRIM(video_url), '') IS NOT NULL), 0::bigint)
)::bigint AS total_videos,
(SELECT COUNT(*)::bigint FROM programs) AS lms_programs,
(SELECT COUNT(*)::bigint FROM courses) AS lms_courses,
(SELECT COUNT(*)::bigint FROM modules) AS lms_modules,
(SELECT COUNT(*)::bigint FROM lessons) AS lms_lessons,
(SELECT COUNT(*)::bigint FROM lessons WHERE NULLIF(BTRIM(video_url), '') IS NOT NULL) AS lms_lessons_with_video,
(SELECT COUNT(*)::bigint FROM lms_practices) AS lms_practices,
(SELECT COUNT(*)::bigint FROM lms_practices WHERE course_id IS NOT NULL) AS lms_practices_at_course,
(SELECT COUNT(*)::bigint FROM lms_practices WHERE module_id IS NOT NULL) AS lms_practices_at_module,
(SELECT COUNT(*)::bigint FROM lms_practices WHERE lesson_id IS NOT NULL) AS lms_practices_at_lesson,
(SELECT COUNT(*)::bigint FROM exam_prep.catalog_courses) AS exam_prep_catalog_courses,
(SELECT COUNT(*)::bigint FROM exam_prep.units) AS exam_prep_units,
(SELECT COUNT(*)::bigint FROM exam_prep.unit_modules) AS exam_prep_unit_modules,
(SELECT COUNT(*)::bigint FROM exam_prep.unit_module_lessons) AS exam_prep_lessons,
(SELECT COUNT(*)::bigint FROM exam_prep.unit_module_lessons WHERE NULLIF(BTRIM(video_url), '') IS NOT NULL) AS exam_prep_lessons_with_video,
(SELECT COUNT(*)::bigint FROM exam_prep.lesson_practices) AS exam_prep_lesson_practices
`
type AnalyticsCourseCountsRow struct {
TotalCategories int64 `json:"total_categories"`
TotalCourses int64 `json:"total_courses"`
TotalSubCourses int64 `json:"total_sub_courses"`
TotalVideos int64 `json:"total_videos"`
LmsPrograms int64 `json:"lms_programs"`
LmsCourses int64 `json:"lms_courses"`
LmsModules int64 `json:"lms_modules"`
LmsLessons int64 `json:"lms_lessons"`
LmsLessonsWithVideo int64 `json:"lms_lessons_with_video"`
LmsPractices int64 `json:"lms_practices"`
LmsPracticesAtCourse int64 `json:"lms_practices_at_course"`
LmsPracticesAtModule int64 `json:"lms_practices_at_module"`
LmsPracticesAtLesson int64 `json:"lms_practices_at_lesson"`
ExamPrepCatalogCourses int64 `json:"exam_prep_catalog_courses"`
ExamPrepUnits int64 `json:"exam_prep_units"`
ExamPrepUnitModules int64 `json:"exam_prep_unit_modules"`
ExamPrepLessons int64 `json:"exam_prep_lessons"`
ExamPrepLessonsWithVideo int64 `json:"exam_prep_lessons_with_video"`
ExamPrepLessonPractices int64 `json:"exam_prep_lesson_practices"`
}
// =====================
// Course Analytics
// =====================
// LMS: programs -> courses -> modules -> lessons; lms_practices attach to course, module, or lesson.
// Exam prep: exam_prep.catalog_courses -> units -> unit_modules -> unit_module_lessons; exam_prep.lesson_practices.
// Legacy dashboard fields map to: programs, courses, modules, and video-bearing lessons (LMS + exam prep).
func (q *Queries) AnalyticsCourseCounts(ctx context.Context) (AnalyticsCourseCountsRow, error) {
row := q.db.QueryRow(ctx, AnalyticsCourseCounts)
var i AnalyticsCourseCountsRow
err := row.Scan(
&i.TotalCategories,
&i.TotalCourses,
&i.TotalSubCourses,
&i.TotalVideos,
&i.LmsPrograms,
&i.LmsCourses,
&i.LmsModules,
&i.LmsLessons,
&i.LmsLessonsWithVideo,
&i.LmsPractices,
&i.LmsPracticesAtCourse,
&i.LmsPracticesAtModule,
&i.LmsPracticesAtLesson,
&i.ExamPrepCatalogCourses,
&i.ExamPrepUnits,
&i.ExamPrepUnitModules,
&i.ExamPrepLessons,
&i.ExamPrepLessonsWithVideo,
&i.ExamPrepLessonPractices,
)
return i, err
}
const AnalyticsIssuesByStatus = `-- name: AnalyticsIssuesByStatus :many
SELECT
COALESCE(ri.status, 'unknown') AS status,
COUNT(*)::bigint AS count
FROM reported_issues ri
WHERE ($1::timestamptz IS NULL OR ri.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR ri.created_at < $2::timestamptz)
GROUP BY ri.status
ORDER BY count DESC
`
type AnalyticsIssuesByStatusParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsIssuesByStatusRow struct {
Status string `json:"status"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsIssuesByStatus(ctx context.Context, arg AnalyticsIssuesByStatusParams) ([]AnalyticsIssuesByStatusRow, error) {
rows, err := q.db.Query(ctx, AnalyticsIssuesByStatus, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsIssuesByStatusRow
for rows.Next() {
var i AnalyticsIssuesByStatusRow
if err := rows.Scan(&i.Status, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsIssuesByType = `-- name: AnalyticsIssuesByType :many
SELECT
COALESCE(ri.issue_type, 'unknown') AS issue_type,
COUNT(*)::bigint AS count
FROM reported_issues ri
WHERE ($1::timestamptz IS NULL OR ri.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR ri.created_at < $2::timestamptz)
GROUP BY ri.issue_type
ORDER BY count DESC
`
type AnalyticsIssuesByTypeParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsIssuesByTypeRow struct {
IssueType string `json:"issue_type"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsIssuesByType(ctx context.Context, arg AnalyticsIssuesByTypeParams) ([]AnalyticsIssuesByTypeRow, error) {
rows, err := q.db.Query(ctx, AnalyticsIssuesByType, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsIssuesByTypeRow
for rows.Next() {
var i AnalyticsIssuesByTypeRow
if err := rows.Scan(&i.IssueType, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsIssuesSummary = `-- name: AnalyticsIssuesSummary :one
SELECT
COUNT(*)::bigint AS total,
COUNT(*) FILTER (WHERE ri.status = 'resolved')::bigint AS resolved,
CASE
WHEN COUNT(*) > 0 THEN (COUNT(*) FILTER (WHERE ri.status = 'resolved')::float8 / COUNT(*)::float8)
ELSE 0::float8
END AS resolution_rate
FROM reported_issues ri
WHERE ($1::timestamptz IS NULL OR ri.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR ri.created_at < $2::timestamptz)
`
type AnalyticsIssuesSummaryParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsIssuesSummaryRow struct {
Total int64 `json:"total"`
Resolved int64 `json:"resolved"`
ResolutionRate float64 `json:"resolution_rate"`
}
// =====================
// Issue Analytics
// =====================
func (q *Queries) AnalyticsIssuesSummary(ctx context.Context, arg AnalyticsIssuesSummaryParams) (AnalyticsIssuesSummaryRow, error) {
row := q.db.QueryRow(ctx, AnalyticsIssuesSummary, arg.RangeStart, arg.RangeEnd)
var i AnalyticsIssuesSummaryRow
err := row.Scan(&i.Total, &i.Resolved, &i.ResolutionRate)
return i, err
}
const AnalyticsNewSubscriptionsLast30Days = `-- name: AnalyticsNewSubscriptionsLast30Days :many
SELECT
d.date,
COUNT(us.id)::bigint AS count
FROM generate_series(
$1::date,
$2::date,
INTERVAL '1 day'
) AS d(date)
LEFT JOIN user_subscriptions us ON us.created_at::date = d.date
AND ($3::timestamptz IS NULL OR us.created_at >= $3::timestamptz)
AND ($4::timestamptz IS NULL OR us.created_at < $4::timestamptz)
GROUP BY d.date
ORDER BY d.date
`
type AnalyticsNewSubscriptionsLast30DaysParams struct {
SeriesStart pgtype.Date `json:"series_start"`
SeriesEnd pgtype.Date `json:"series_end"`
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsNewSubscriptionsLast30DaysRow struct {
Date interface{} `json:"date"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsNewSubscriptionsLast30Days(ctx context.Context, arg AnalyticsNewSubscriptionsLast30DaysParams) ([]AnalyticsNewSubscriptionsLast30DaysRow, error) {
rows, err := q.db.Query(ctx, AnalyticsNewSubscriptionsLast30Days,
arg.SeriesStart,
arg.SeriesEnd,
arg.RangeStart,
arg.RangeEnd,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsNewSubscriptionsLast30DaysRow
for rows.Next() {
var i AnalyticsNewSubscriptionsLast30DaysRow
if err := rows.Scan(&i.Date, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsNotificationsByChannel = `-- name: AnalyticsNotificationsByChannel :many
SELECT
COALESCE(n.channel, 'unknown') AS channel,
COUNT(*)::bigint AS count
FROM notifications n
WHERE ($1::timestamptz IS NULL OR n.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR n.created_at < $2::timestamptz)
GROUP BY n.channel
ORDER BY count DESC
`
type AnalyticsNotificationsByChannelParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsNotificationsByChannelRow struct {
Channel string `json:"channel"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsNotificationsByChannel(ctx context.Context, arg AnalyticsNotificationsByChannelParams) ([]AnalyticsNotificationsByChannelRow, error) {
rows, err := q.db.Query(ctx, AnalyticsNotificationsByChannel, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsNotificationsByChannelRow
for rows.Next() {
var i AnalyticsNotificationsByChannelRow
if err := rows.Scan(&i.Channel, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsNotificationsByType = `-- name: AnalyticsNotificationsByType :many
SELECT
COALESCE(n.type, 'unknown') AS type,
COUNT(*)::bigint AS count
FROM notifications n
WHERE ($1::timestamptz IS NULL OR n.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR n.created_at < $2::timestamptz)
GROUP BY n.type
ORDER BY count DESC
`
type AnalyticsNotificationsByTypeParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsNotificationsByTypeRow struct {
Type string `json:"type"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsNotificationsByType(ctx context.Context, arg AnalyticsNotificationsByTypeParams) ([]AnalyticsNotificationsByTypeRow, error) {
rows, err := q.db.Query(ctx, AnalyticsNotificationsByType, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsNotificationsByTypeRow
for rows.Next() {
var i AnalyticsNotificationsByTypeRow
if err := rows.Scan(&i.Type, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsNotificationsSummary = `-- name: AnalyticsNotificationsSummary :one
SELECT
COUNT(*)::bigint AS total,
COUNT(*) FILTER (WHERE n.is_read = TRUE)::bigint AS read,
COUNT(*) FILTER (WHERE n.is_read = FALSE)::bigint AS unread
FROM notifications n
WHERE ($1::timestamptz IS NULL OR n.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR n.created_at < $2::timestamptz)
`
type AnalyticsNotificationsSummaryParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsNotificationsSummaryRow struct {
Total int64 `json:"total"`
Read int64 `json:"read"`
Unread int64 `json:"unread"`
}
// =====================
// Notification Analytics
// =====================
func (q *Queries) AnalyticsNotificationsSummary(ctx context.Context, arg AnalyticsNotificationsSummaryParams) (AnalyticsNotificationsSummaryRow, error) {
row := q.db.QueryRow(ctx, AnalyticsNotificationsSummary, arg.RangeStart, arg.RangeEnd)
var i AnalyticsNotificationsSummaryRow
err := row.Scan(&i.Total, &i.Read, &i.Unread)
return i, err
}
const AnalyticsPaymentsByMethod = `-- name: AnalyticsPaymentsByMethod :many
SELECT
COALESCE(p.payment_method, 'unknown') AS payment_method,
COUNT(*)::bigint AS count,
COALESCE(SUM(p.amount), 0)::float8 AS total_amount
FROM payments p
WHERE p.status = 'SUCCESS'
AND ($1::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) < $2::timestamptz)
GROUP BY p.payment_method
ORDER BY count DESC
`
type AnalyticsPaymentsByMethodParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsPaymentsByMethodRow struct {
PaymentMethod string `json:"payment_method"`
Count int64 `json:"count"`
TotalAmount float64 `json:"total_amount"`
}
func (q *Queries) AnalyticsPaymentsByMethod(ctx context.Context, arg AnalyticsPaymentsByMethodParams) ([]AnalyticsPaymentsByMethodRow, error) {
rows, err := q.db.Query(ctx, AnalyticsPaymentsByMethod, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsPaymentsByMethodRow
for rows.Next() {
var i AnalyticsPaymentsByMethodRow
if err := rows.Scan(&i.PaymentMethod, &i.Count, &i.TotalAmount); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsPaymentsByStatus = `-- name: AnalyticsPaymentsByStatus :many
SELECT
COALESCE(p.status, 'unknown') AS status,
COUNT(*)::bigint AS count,
COALESCE(SUM(p.amount), 0)::float8 AS total_amount
FROM payments p
WHERE ($1::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) < $2::timestamptz)
GROUP BY p.status
ORDER BY count DESC
`
type AnalyticsPaymentsByStatusParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsPaymentsByStatusRow struct {
Status string `json:"status"`
Count int64 `json:"count"`
TotalAmount float64 `json:"total_amount"`
}
func (q *Queries) AnalyticsPaymentsByStatus(ctx context.Context, arg AnalyticsPaymentsByStatusParams) ([]AnalyticsPaymentsByStatusRow, error) {
rows, err := q.db.Query(ctx, AnalyticsPaymentsByStatus, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsPaymentsByStatusRow
for rows.Next() {
var i AnalyticsPaymentsByStatusRow
if err := rows.Scan(&i.Status, &i.Count, &i.TotalAmount); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsPaymentsSummary = `-- name: AnalyticsPaymentsSummary :one
SELECT
COALESCE(SUM(p.amount) FILTER (WHERE p.status = 'SUCCESS'), 0)::float8 AS total_revenue,
COALESCE(AVG(p.amount) FILTER (WHERE p.status = 'SUCCESS'), 0)::float8 AS avg_value,
COUNT(*)::bigint AS total_payments,
COUNT(*) FILTER (WHERE p.status = 'SUCCESS')::bigint AS successful_payments
FROM payments p
WHERE ($1::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) < $2::timestamptz)
`
type AnalyticsPaymentsSummaryParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsPaymentsSummaryRow struct {
TotalRevenue float64 `json:"total_revenue"`
AvgValue float64 `json:"avg_value"`
TotalPayments int64 `json:"total_payments"`
SuccessfulPayments int64 `json:"successful_payments"`
}
// =====================
// Payment Analytics
// =====================
func (q *Queries) AnalyticsPaymentsSummary(ctx context.Context, arg AnalyticsPaymentsSummaryParams) (AnalyticsPaymentsSummaryRow, error) {
row := q.db.QueryRow(ctx, AnalyticsPaymentsSummary, arg.RangeStart, arg.RangeEnd)
var i AnalyticsPaymentsSummaryRow
err := row.Scan(
&i.TotalRevenue,
&i.AvgValue,
&i.TotalPayments,
&i.SuccessfulPayments,
)
return i, err
}
const AnalyticsQuestionSetsByType = `-- name: AnalyticsQuestionSetsByType :many
SELECT
COALESCE(qs.set_type, 'unknown') AS set_type,
COUNT(*)::bigint AS count
FROM question_sets qs
WHERE ($1::timestamptz IS NULL OR qs.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR qs.created_at < $2::timestamptz)
GROUP BY qs.set_type
ORDER BY count DESC
`
type AnalyticsQuestionSetsByTypeParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsQuestionSetsByTypeRow struct {
SetType string `json:"set_type"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsQuestionSetsByType(ctx context.Context, arg AnalyticsQuestionSetsByTypeParams) ([]AnalyticsQuestionSetsByTypeRow, error) {
rows, err := q.db.Query(ctx, AnalyticsQuestionSetsByType, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsQuestionSetsByTypeRow
for rows.Next() {
var i AnalyticsQuestionSetsByTypeRow
if err := rows.Scan(&i.SetType, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsQuestionsByType = `-- name: AnalyticsQuestionsByType :many
SELECT
COALESCE(q.question_type, 'unknown') AS question_type,
COUNT(*)::bigint AS count
FROM questions q
WHERE ($1::timestamptz IS NULL OR q.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR q.created_at < $2::timestamptz)
GROUP BY q.question_type
ORDER BY count DESC
`
type AnalyticsQuestionsByTypeParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsQuestionsByTypeRow struct {
QuestionType string `json:"question_type"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsQuestionsByType(ctx context.Context, arg AnalyticsQuestionsByTypeParams) ([]AnalyticsQuestionsByTypeRow, error) {
rows, err := q.db.Query(ctx, AnalyticsQuestionsByType, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsQuestionsByTypeRow
for rows.Next() {
var i AnalyticsQuestionsByTypeRow
if err := rows.Scan(&i.QuestionType, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsQuestionsCounts = `-- name: AnalyticsQuestionsCounts :one
SELECT
(
SELECT COUNT(*)::bigint
FROM questions q
WHERE ($1::timestamptz IS NULL OR q.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR q.created_at < $2::timestamptz)
) AS total_questions,
(
SELECT COUNT(*)::bigint
FROM question_sets qs
WHERE ($1::timestamptz IS NULL OR qs.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR qs.created_at < $2::timestamptz)
) AS total_question_sets
`
type AnalyticsQuestionsCountsParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsQuestionsCountsRow struct {
TotalQuestions int64 `json:"total_questions"`
TotalQuestionSets int64 `json:"total_question_sets"`
}
// =====================
// Content Analytics
// =====================
func (q *Queries) AnalyticsQuestionsCounts(ctx context.Context, arg AnalyticsQuestionsCountsParams) (AnalyticsQuestionsCountsRow, error) {
row := q.db.QueryRow(ctx, AnalyticsQuestionsCounts, arg.RangeStart, arg.RangeEnd)
var i AnalyticsQuestionsCountsRow
err := row.Scan(&i.TotalQuestions, &i.TotalQuestionSets)
return i, err
}
const AnalyticsRevenueByPlan = `-- name: AnalyticsRevenueByPlan :many
SELECT
sp.name AS plan_name,
sp.currency,
COUNT(p.id)::bigint AS total_payments,
COALESCE(SUM(p.amount), 0)::float8 AS total_revenue
FROM payments p
JOIN subscription_plans sp ON sp.id = p.plan_id
WHERE p.status = 'SUCCESS'
AND ($1::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) < $2::timestamptz)
GROUP BY sp.name, sp.currency
ORDER BY total_revenue DESC
`
type AnalyticsRevenueByPlanParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsRevenueByPlanRow struct {
PlanName string `json:"plan_name"`
Currency string `json:"currency"`
TotalPayments int64 `json:"total_payments"`
TotalRevenue float64 `json:"total_revenue"`
}
func (q *Queries) AnalyticsRevenueByPlan(ctx context.Context, arg AnalyticsRevenueByPlanParams) ([]AnalyticsRevenueByPlanRow, error) {
rows, err := q.db.Query(ctx, AnalyticsRevenueByPlan, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsRevenueByPlanRow
for rows.Next() {
var i AnalyticsRevenueByPlanRow
if err := rows.Scan(
&i.PlanName,
&i.Currency,
&i.TotalPayments,
&i.TotalRevenue,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsRevenueLast30Days = `-- name: AnalyticsRevenueLast30Days :many
SELECT
d.date,
COALESCE(SUM(p.amount), 0)::float8 AS total_revenue
FROM generate_series(
$1::date,
$2::date,
INTERVAL '1 day'
) AS d(date)
LEFT JOIN payments p ON COALESCE(p.paid_at, p.created_at)::date = d.date
AND p.status = 'SUCCESS'
AND ($3::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) >= $3::timestamptz)
AND ($4::timestamptz IS NULL OR COALESCE(p.paid_at, p.created_at) < $4::timestamptz)
GROUP BY d.date
ORDER BY d.date
`
type AnalyticsRevenueLast30DaysParams struct {
SeriesStart pgtype.Date `json:"series_start"`
SeriesEnd pgtype.Date `json:"series_end"`
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsRevenueLast30DaysRow struct {
Date interface{} `json:"date"`
TotalRevenue float64 `json:"total_revenue"`
}
func (q *Queries) AnalyticsRevenueLast30Days(ctx context.Context, arg AnalyticsRevenueLast30DaysParams) ([]AnalyticsRevenueLast30DaysRow, error) {
rows, err := q.db.Query(ctx, AnalyticsRevenueLast30Days,
arg.SeriesStart,
arg.SeriesEnd,
arg.RangeStart,
arg.RangeEnd,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsRevenueLast30DaysRow
for rows.Next() {
var i AnalyticsRevenueLast30DaysRow
if err := rows.Scan(&i.Date, &i.TotalRevenue); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsRevenueMonthlyByYear = `-- name: AnalyticsRevenueMonthlyByYear :many
WITH months AS (
SELECT bucket
FROM generate_series(
make_timestamptz($1::int, 1, 1, 0, 0, 0, 'UTC'),
make_timestamptz($1::int, 12, 1, 0, 0, 0, 'UTC'),
INTERVAL '1 month'
) AS gs(bucket)
),
by_month_currency AS (
SELECT
date_trunc('month', COALESCE(p.paid_at, p.created_at) AT TIME ZONE 'UTC') AS ym,
p.currency,
SUM(p.amount)::float8 AS total_revenue
FROM payments p
WHERE p.status = 'SUCCESS'
AND EXTRACT(YEAR FROM COALESCE(p.paid_at, p.created_at) AT TIME ZONE 'UTC')::int = $1::int
GROUP BY 1, 2
)
SELECT
(EXTRACT(MONTH FROM m.bucket AT TIME ZONE 'UTC'))::int AS month,
date_trunc('month', m.bucket AT TIME ZONE 'UTC')::date AS month_start,
COALESCE(b.currency, 'ETB'::varchar) AS currency,
COALESCE(b.total_revenue, 0)::float8 AS total_revenue
FROM months m
LEFT JOIN by_month_currency b ON b.ym = date_trunc('month', m.bucket AT TIME ZONE 'UTC')
ORDER BY m.bucket, COALESCE(b.currency, ''::varchar)
`
type AnalyticsRevenueMonthlyByYearRow struct {
Month int32 `json:"month"`
MonthStart pgtype.Date `json:"month_start"`
Currency string `json:"currency"`
TotalRevenue float64 `json:"total_revenue"`
}
// Monthly successful revenue for a calendar year (UTC buckets). Months with multiple currencies emit one row each; months with no revenue emit one row (currency ETB, revenue 0).
func (q *Queries) AnalyticsRevenueMonthlyByYear(ctx context.Context, reportYear int32) ([]AnalyticsRevenueMonthlyByYearRow, error) {
rows, err := q.db.Query(ctx, AnalyticsRevenueMonthlyByYear, reportYear)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsRevenueMonthlyByYearRow
for rows.Next() {
var i AnalyticsRevenueMonthlyByYearRow
if err := rows.Scan(
&i.Month,
&i.MonthStart,
&i.Currency,
&i.TotalRevenue,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsSubscriptionsByStatus = `-- name: AnalyticsSubscriptionsByStatus :many
SELECT
COALESCE(us.status, 'unknown') AS status,
COUNT(*)::bigint AS count
FROM user_subscriptions us
WHERE ($1::timestamptz IS NULL OR us.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR us.created_at < $2::timestamptz)
GROUP BY us.status
ORDER BY count DESC
`
type AnalyticsSubscriptionsByStatusParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsSubscriptionsByStatusRow struct {
Status string `json:"status"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsSubscriptionsByStatus(ctx context.Context, arg AnalyticsSubscriptionsByStatusParams) ([]AnalyticsSubscriptionsByStatusRow, error) {
rows, err := q.db.Query(ctx, AnalyticsSubscriptionsByStatus, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsSubscriptionsByStatusRow
for rows.Next() {
var i AnalyticsSubscriptionsByStatusRow
if err := rows.Scan(&i.Status, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsSubscriptionsSummary = `-- name: AnalyticsSubscriptionsSummary :one
SELECT
COUNT(*)::bigint AS total,
COUNT(*) FILTER (WHERE us.status = 'ACTIVE')::bigint AS active,
COUNT(*) FILTER (WHERE us.created_at::date = $1::date)::bigint AS new_today,
COUNT(*) FILTER (
WHERE us.created_at::date >= $1::date - INTERVAL '6 days'
AND us.created_at::date <= $1::date
)::bigint AS new_this_week,
COUNT(*) FILTER (
WHERE us.created_at::date >= $1::date - INTERVAL '29 days'
AND us.created_at::date <= $1::date
)::bigint AS new_this_month
FROM user_subscriptions us
WHERE ($2::timestamptz IS NULL OR us.created_at >= $2::timestamptz)
AND ($3::timestamptz IS NULL OR us.created_at < $3::timestamptz)
`
type AnalyticsSubscriptionsSummaryParams struct {
RefDate pgtype.Date `json:"ref_date"`
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsSubscriptionsSummaryRow struct {
Total int64 `json:"total"`
Active int64 `json:"active"`
NewToday int64 `json:"new_today"`
NewThisWeek int64 `json:"new_this_week"`
NewThisMonth int64 `json:"new_this_month"`
}
// =====================
// Subscription Analytics
// =====================
func (q *Queries) AnalyticsSubscriptionsSummary(ctx context.Context, arg AnalyticsSubscriptionsSummaryParams) (AnalyticsSubscriptionsSummaryRow, error) {
row := q.db.QueryRow(ctx, AnalyticsSubscriptionsSummary, arg.RefDate, arg.RangeStart, arg.RangeEnd)
var i AnalyticsSubscriptionsSummaryRow
err := row.Scan(
&i.Total,
&i.Active,
&i.NewToday,
&i.NewThisWeek,
&i.NewThisMonth,
)
return i, err
}
const AnalyticsTeamByRole = `-- name: AnalyticsTeamByRole :many
SELECT
COALESCE(tm.team_role, 'unknown') AS team_role,
COUNT(*)::bigint AS count
FROM team_members tm
WHERE ($1::timestamptz IS NULL OR tm.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR tm.created_at < $2::timestamptz)
GROUP BY tm.team_role
ORDER BY count DESC
`
type AnalyticsTeamByRoleParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsTeamByRoleRow struct {
TeamRole string `json:"team_role"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsTeamByRole(ctx context.Context, arg AnalyticsTeamByRoleParams) ([]AnalyticsTeamByRoleRow, error) {
rows, err := q.db.Query(ctx, AnalyticsTeamByRole, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsTeamByRoleRow
for rows.Next() {
var i AnalyticsTeamByRoleRow
if err := rows.Scan(&i.TeamRole, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsTeamByStatus = `-- name: AnalyticsTeamByStatus :many
SELECT
COALESCE(tm.status, 'unknown') AS status,
COUNT(*)::bigint AS count
FROM team_members tm
WHERE ($1::timestamptz IS NULL OR tm.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR tm.created_at < $2::timestamptz)
GROUP BY tm.status
ORDER BY count DESC
`
type AnalyticsTeamByStatusParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsTeamByStatusRow struct {
Status string `json:"status"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsTeamByStatus(ctx context.Context, arg AnalyticsTeamByStatusParams) ([]AnalyticsTeamByStatusRow, error) {
rows, err := q.db.Query(ctx, AnalyticsTeamByStatus, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsTeamByStatusRow
for rows.Next() {
var i AnalyticsTeamByStatusRow
if err := rows.Scan(&i.Status, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsTeamSummary = `-- name: AnalyticsTeamSummary :one
SELECT
COUNT(*)::bigint AS total_members
FROM team_members tm
WHERE ($1::timestamptz IS NULL OR tm.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR tm.created_at < $2::timestamptz)
`
type AnalyticsTeamSummaryParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
// =====================
// Team Analytics
// =====================
func (q *Queries) AnalyticsTeamSummary(ctx context.Context, arg AnalyticsTeamSummaryParams) (int64, error) {
row := q.db.QueryRow(ctx, AnalyticsTeamSummary, arg.RangeStart, arg.RangeEnd)
var total_members int64
err := row.Scan(&total_members)
return total_members, err
}
const AnalyticsUserRegistrationsLast30Days = `-- name: AnalyticsUserRegistrationsLast30Days :many
SELECT
d.date,
COUNT(u.id)::bigint AS count
FROM generate_series(
$1::date,
$2::date,
INTERVAL '1 day'
) AS d(date)
LEFT JOIN users u ON u.created_at::date = d.date
AND ($3::timestamptz IS NULL OR u.created_at >= $3::timestamptz)
AND ($4::timestamptz IS NULL OR u.created_at < $4::timestamptz)
GROUP BY d.date
ORDER BY d.date
`
type AnalyticsUserRegistrationsLast30DaysParams struct {
SeriesStart pgtype.Date `json:"series_start"`
SeriesEnd pgtype.Date `json:"series_end"`
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUserRegistrationsLast30DaysRow struct {
Date interface{} `json:"date"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUserRegistrationsLast30Days(ctx context.Context, arg AnalyticsUserRegistrationsLast30DaysParams) ([]AnalyticsUserRegistrationsLast30DaysRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUserRegistrationsLast30Days,
arg.SeriesStart,
arg.SeriesEnd,
arg.RangeStart,
arg.RangeEnd,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUserRegistrationsLast30DaysRow
for rows.Next() {
var i AnalyticsUserRegistrationsLast30DaysRow
if err := rows.Scan(&i.Date, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersByAgeGroup = `-- name: AnalyticsUsersByAgeGroup :many
SELECT
COALESCE(u.age_group, 'unknown') AS age_group,
COUNT(*)::bigint AS count
FROM users u
WHERE ($1::timestamptz IS NULL OR u.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR u.created_at < $2::timestamptz)
GROUP BY u.age_group
ORDER BY count DESC
`
type AnalyticsUsersByAgeGroupParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersByAgeGroupRow struct {
AgeGroup string `json:"age_group"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUsersByAgeGroup(ctx context.Context, arg AnalyticsUsersByAgeGroupParams) ([]AnalyticsUsersByAgeGroupRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUsersByAgeGroup, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUsersByAgeGroupRow
for rows.Next() {
var i AnalyticsUsersByAgeGroupRow
if err := rows.Scan(&i.AgeGroup, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersByEducationLevel = `-- name: AnalyticsUsersByEducationLevel :many
SELECT
COALESCE(NULLIF(TRIM(u.education_level), ''), 'unknown')::text AS education_level,
COUNT(*)::bigint AS count
FROM users u
WHERE ($1::timestamptz IS NULL OR u.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR u.created_at < $2::timestamptz)
GROUP BY COALESCE(NULLIF(TRIM(u.education_level), ''), 'unknown')
ORDER BY count DESC
`
type AnalyticsUsersByEducationLevelParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersByEducationLevelRow struct {
EducationLevel string `json:"education_level"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUsersByEducationLevel(ctx context.Context, arg AnalyticsUsersByEducationLevelParams) ([]AnalyticsUsersByEducationLevelRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUsersByEducationLevel, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUsersByEducationLevelRow
for rows.Next() {
var i AnalyticsUsersByEducationLevelRow
if err := rows.Scan(&i.EducationLevel, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersByKnowledgeLevel = `-- name: AnalyticsUsersByKnowledgeLevel :many
SELECT
COALESCE(u.knowledge_level, 'unknown') AS knowledge_level,
COUNT(*)::bigint AS count
FROM users u
WHERE ($1::timestamptz IS NULL OR u.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR u.created_at < $2::timestamptz)
GROUP BY u.knowledge_level
ORDER BY count DESC
`
type AnalyticsUsersByKnowledgeLevelParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersByKnowledgeLevelRow struct {
KnowledgeLevel string `json:"knowledge_level"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUsersByKnowledgeLevel(ctx context.Context, arg AnalyticsUsersByKnowledgeLevelParams) ([]AnalyticsUsersByKnowledgeLevelRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUsersByKnowledgeLevel, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUsersByKnowledgeLevelRow
for rows.Next() {
var i AnalyticsUsersByKnowledgeLevelRow
if err := rows.Scan(&i.KnowledgeLevel, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersByLanguageChallange = `-- name: AnalyticsUsersByLanguageChallange :many
SELECT
COALESCE(NULLIF(TRIM(u.language_challange), ''), 'unknown')::text AS language_challange,
COUNT(*)::bigint AS count
FROM users u
WHERE ($1::timestamptz IS NULL OR u.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR u.created_at < $2::timestamptz)
GROUP BY COALESCE(NULLIF(TRIM(u.language_challange), ''), 'unknown')
ORDER BY count DESC
`
type AnalyticsUsersByLanguageChallangeParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersByLanguageChallangeRow struct {
LanguageChallange string `json:"language_challange"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUsersByLanguageChallange(ctx context.Context, arg AnalyticsUsersByLanguageChallangeParams) ([]AnalyticsUsersByLanguageChallangeRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUsersByLanguageChallange, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUsersByLanguageChallangeRow
for rows.Next() {
var i AnalyticsUsersByLanguageChallangeRow
if err := rows.Scan(&i.LanguageChallange, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersByLearningGoal = `-- name: AnalyticsUsersByLearningGoal :many
SELECT
COALESCE(NULLIF(TRIM(u.learning_goal), ''), 'unknown')::text AS learning_goal,
COUNT(*)::bigint AS count
FROM users u
WHERE ($1::timestamptz IS NULL OR u.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR u.created_at < $2::timestamptz)
GROUP BY COALESCE(NULLIF(TRIM(u.learning_goal), ''), 'unknown')
ORDER BY count DESC
`
type AnalyticsUsersByLearningGoalParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersByLearningGoalRow struct {
LearningGoal string `json:"learning_goal"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUsersByLearningGoal(ctx context.Context, arg AnalyticsUsersByLearningGoalParams) ([]AnalyticsUsersByLearningGoalRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUsersByLearningGoal, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUsersByLearningGoalRow
for rows.Next() {
var i AnalyticsUsersByLearningGoalRow
if err := rows.Scan(&i.LearningGoal, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersByOccupation = `-- name: AnalyticsUsersByOccupation :many
SELECT
COALESCE(NULLIF(TRIM(u.occupation), ''), 'unknown')::text AS occupation,
COUNT(*)::bigint AS count
FROM users u
WHERE ($1::timestamptz IS NULL OR u.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR u.created_at < $2::timestamptz)
GROUP BY COALESCE(NULLIF(TRIM(u.occupation), ''), 'unknown')
ORDER BY count DESC
`
type AnalyticsUsersByOccupationParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersByOccupationRow struct {
Occupation string `json:"occupation"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUsersByOccupation(ctx context.Context, arg AnalyticsUsersByOccupationParams) ([]AnalyticsUsersByOccupationRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUsersByOccupation, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUsersByOccupationRow
for rows.Next() {
var i AnalyticsUsersByOccupationRow
if err := rows.Scan(&i.Occupation, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersByRegion = `-- name: AnalyticsUsersByRegion :many
SELECT
COALESCE(u.region, 'unknown') AS region,
COUNT(*)::bigint AS count
FROM users u
WHERE ($1::timestamptz IS NULL OR u.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR u.created_at < $2::timestamptz)
GROUP BY u.region
ORDER BY count DESC
`
type AnalyticsUsersByRegionParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersByRegionRow struct {
Region string `json:"region"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUsersByRegion(ctx context.Context, arg AnalyticsUsersByRegionParams) ([]AnalyticsUsersByRegionRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUsersByRegion, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUsersByRegionRow
for rows.Next() {
var i AnalyticsUsersByRegionRow
if err := rows.Scan(&i.Region, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersByRole = `-- name: AnalyticsUsersByRole :many
SELECT
COALESCE(u.role, 'unknown') AS role,
COUNT(*)::bigint AS count
FROM users u
WHERE ($1::timestamptz IS NULL OR u.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR u.created_at < $2::timestamptz)
GROUP BY u.role
ORDER BY count DESC
`
type AnalyticsUsersByRoleParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersByRoleRow struct {
Role string `json:"role"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUsersByRole(ctx context.Context, arg AnalyticsUsersByRoleParams) ([]AnalyticsUsersByRoleRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUsersByRole, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUsersByRoleRow
for rows.Next() {
var i AnalyticsUsersByRoleRow
if err := rows.Scan(&i.Role, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersByStatus = `-- name: AnalyticsUsersByStatus :many
SELECT
COALESCE(u.status, 'unknown') AS status,
COUNT(*)::bigint AS count
FROM users u
WHERE ($1::timestamptz IS NULL OR u.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR u.created_at < $2::timestamptz)
GROUP BY u.status
ORDER BY count DESC
`
type AnalyticsUsersByStatusParams struct {
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersByStatusRow struct {
Status string `json:"status"`
Count int64 `json:"count"`
}
func (q *Queries) AnalyticsUsersByStatus(ctx context.Context, arg AnalyticsUsersByStatusParams) ([]AnalyticsUsersByStatusRow, error) {
rows, err := q.db.Query(ctx, AnalyticsUsersByStatus, arg.RangeStart, arg.RangeEnd)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AnalyticsUsersByStatusRow
for rows.Next() {
var i AnalyticsUsersByStatusRow
if err := rows.Scan(&i.Status, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const AnalyticsUsersSummary = `-- name: AnalyticsUsersSummary :one
SELECT
COUNT(*)::bigint AS total,
COUNT(*) FILTER (WHERE u.created_at::date = $1::date)::bigint AS new_today,
COUNT(*) FILTER (
WHERE u.created_at::date >= $1::date - INTERVAL '6 days'
AND u.created_at::date <= $1::date
)::bigint AS new_this_week,
COUNT(*) FILTER (
WHERE u.created_at::date >= $1::date - INTERVAL '29 days'
AND u.created_at::date <= $1::date
)::bigint AS new_this_month
FROM users u
WHERE ($2::timestamptz IS NULL OR u.created_at >= $2::timestamptz)
AND ($3::timestamptz IS NULL OR u.created_at < $3::timestamptz)
`
type AnalyticsUsersSummaryParams struct {
RefDate pgtype.Date `json:"ref_date"`
RangeStart pgtype.Timestamptz `json:"range_start"`
RangeEnd pgtype.Timestamptz `json:"range_end"`
}
type AnalyticsUsersSummaryRow struct {
Total int64 `json:"total"`
NewToday int64 `json:"new_today"`
NewThisWeek int64 `json:"new_this_week"`
NewThisMonth int64 `json:"new_this_month"`
}
// =====================
// Analytics (date-filtered)
// =====================
// Shared optional params (nullable = all-time):
//
// range_start, range_end (exclusive upper bound)
//
// Required chart params:
//
// series_start, series_end (inclusive dates)
//
// Relative window anchor:
//
// ref_date (inclusive date used for new_today/week/month)
//
// =====================
// User Analytics
// =====================
func (q *Queries) AnalyticsUsersSummary(ctx context.Context, arg AnalyticsUsersSummaryParams) (AnalyticsUsersSummaryRow, error) {
row := q.db.QueryRow(ctx, AnalyticsUsersSummary, arg.RefDate, arg.RangeStart, arg.RangeEnd)
var i AnalyticsUsersSummaryRow
err := row.Scan(
&i.Total,
&i.NewToday,
&i.NewThisWeek,
&i.NewThisMonth,
)
return i, err
}