diff --git a/analyticsApi/main.go b/analyticsApi/main.go index c37e45c..ac6e499 100644 --- a/analyticsApi/main.go +++ b/analyticsApi/main.go @@ -11,18 +11,165 @@ import ( "time" ) -// initSessionCount +// initPageViewCount // -// this endpoint is used to aggregate session metadata counts -// it adds the following endpoint to the app: -// GET /api/analytics/sessionCounts +// This endpoint is used to retrieve page view counts for each unique path. +// It adds the following endpoint to the app: +// GET /api/analytics/pageViewCount // -// the endpoint expects the following request data: +// The endpoint expects the following query parameter: +// +// ?startDate=ISO 8601 formatted date (optional) +// +// - `startDate`: An optional start date in ISO 8601 format (e.g., "2024-11-12T15:04:05Z"). +// If provided, the global count reflects views from this date onward. +// If not provided, the global count includes all views. +// +// Response format: +// [ // // { -// "startDate": "start date in ISO 8601 format", +// "id": , +// "data": "", +// "count": , +// "last_30_days_data": [ +// { +// "date": "YYYY-MM-DDTHH:MM:SSZ", +// "count": +// }, +// ... +// ] +// }, +// ... +// +// ] +// +// `last_30_days_data` always provides daily counts for the most recent 30 days regardless of the `startDate` parameter. +func initPageViewCount(app *pocketbase.PocketBase, e *core.ServeEvent) { + + e.Router.GET("/api/analytics/pageViewCount", func(c echo.Context) error { + + // Check if user is in admin group + if err := ldapApi.UserIsInAdminGroup(app, c); err != nil { + return err + } + + // Get startDate from query parameter + startDateParam := c.QueryParam("startDate") + var startDate time.Time + var err error + if startDateParam != "" { + // Parse the startDate, expecting an ISO 8601 format + startDate, err = time.Parse(time.RFC3339, startDateParam) + if err != nil { + return apis.NewBadRequestError("Invalid start date format, expected ISO 8601", err) + } + } + + type PageViewData struct { + ID int `json:"id"` + Data string `json:"data"` + Count int `json:"count"` + Last30DaysData interface{} `json:"last_30_days_data"` + } + + var result []PageViewData + + // Execute the query with nested JSON aggregation + err = app.Dao().DB(). + NewQuery(` + SELECT + (ROW_NUMBER() OVER()) AS id, + view.path AS data, + ( + SELECT COUNT(id) + FROM analyticsPageViews + WHERE path = view.path AND created >= {:startDate} + ) AS count, + ( + SELECT json_group_array( + json_object( + 'date', strftime('%Y-%m-%dT%H:%M:%SZ', created), + 'count', daily_count + ) + ) + FROM ( + SELECT + strftime('%Y-%m-%dT%H:%M:%SZ', created) AS date, + COUNT(id) AS daily_count + FROM analyticsPageViews + WHERE + path = view.path AND + created >= datetime('now', '-30 days') + GROUP BY date + ) AS daily_data + ) AS last_30_days_data + FROM + analyticsPageViews view + GROUP BY + view.path + `). + Bind(dbx.Params{ + "startDate": startDate, + }). + All(&result) + + if err != nil { + return apis.NewApiError(500, "Failed to query page view data", err) + } + + // Return the final JSON response + return c.JSON(200, result) + }, apis.ActivityLogger(app)) +} + +// initAggregateCount +// +// This endpoint is used to aggregate session metadata counts by various categories +// (device type, browser name, operating system, user agent, geo country code, and preferred language). +// It adds the following endpoint to the app: +// GET /api/analytics/sessionCounts +// +// The endpoint expects the following query parameter: +// +// ?startDate=ISO 8601 formatted date (optional) +// +// - `startDate`: An optional start date in ISO 8601 format (e.g., "2024-11-12T15:04:05Z"). +// If provided, counts reflect sessions from this date onward. +// If not provided, counts include all sessions. +// +// Response format: +// +// { +// "device_type": [ +// { "value": "", "count": }, +// ... +// ], +// "browser_name": [ +// { "value": "", "count": }, +// ... +// ], +// "operating_system": [ +// { "value": "", "count": }, +// ... +// ], +// "user_agent": [ +// { "value": "", "count": }, +// ... +// ], +// "geo_country_code": [ +// { "value": "", "count": }, +// ... +// ], +// "preferred_language": [ +// { "value": "", "count": }, +// ... +// ] // } -func initSessionCount(app *pocketbase.PocketBase, e *core.ServeEvent) { +// +// Each category includes an array of objects where `value` is the specific item (e.g., a device type or browser name) +// and `count` is the number of sessions matching that item since `startDate`. +func initAggregateCount(app *pocketbase.PocketBase, e *core.ServeEvent) { e.Router.GET("/api/analytics/aggregateCount", func(c echo.Context) error { // Check if user is in admin group @@ -44,7 +191,7 @@ func initSessionCount(app *pocketbase.PocketBase, e *core.ServeEvent) { // Use a map to store the response data response := make(map[string][]Data) - fields := []string{"device_type", "browser_name", "operating_system", "user_agent", "geo_country_code", "preferred_language"} + fields := []string{"device_type", "browser_name", "operating_system", "geo_country_code", "preferred_language"} for _, field := range fields { data := []Data{} @@ -79,7 +226,8 @@ func InitAnalyticsApi(app *pocketbase.PocketBase, e *core.ServeEvent) error { logger.LogInfoF("Initializing analytics api") - initSessionCount(app, e) + initPageViewCount(app, e) + initAggregateCount(app, e) return nil }