diff --git a/api/analytics/hospitality.controller.go b/api/analytics/hospitality.controller.go new file mode 100644 index 00000000..3619fcdc --- /dev/null +++ b/api/analytics/hospitality.controller.go @@ -0,0 +1,68 @@ +package api + +import ( + "context" + "net/http" + "time" + + "github.com/Thanus-Kumaar/anokha-2025-backend/cmd" + db "github.com/Thanus-Kumaar/anokha-2025-backend/db/gen" + "github.com/Thanus-Kumaar/anokha-2025-backend/pkg" + "github.com/gin-gonic/gin" +) + +func GetInsideCampusAnalytics(c *gin.Context) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + conn, err := cmd.DBPool.Acquire(ctx) + if pkg.HandleDbAcquireErr(c, err, "ANALYTICS") { + return + } + defer conn.Release() + + q := db.New() + + insideCampusSummary, err := q.GetInsideCampusAnalyticsQuery(ctx, conn) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "Oops! Something happened. Please try again later", + }) + pkg.Log.ErrorCtx(c, "[ANALYTICS-ERROR]: Failed to get inside campus summary", err) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "Successfully fetched inside campus analytics", + "inside_campus_summary": insideCampusSummary, + }) + pkg.Log.SuccessCtx(c) +} + +func GetLiveBedsAnalytics(c *gin.Context) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + conn, err := cmd.DBPool.Acquire(ctx) + if pkg.HandleDbAcquireErr(c, err, "ANALYTICS") { + return + } + defer conn.Release() + + q := db.New() + + liveBedsSummary, err := q.GetLiveBedsAnalyticsQuery(ctx, conn) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "Oops! Something happened. Please try again later", + }) + pkg.Log.ErrorCtx(c, "[ANALYTICS-ERROR]: Failed to get live beds summary", err) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "Successfully fetched live beds analytics", + "live_beds_summary": liveBedsSummary, + }) + pkg.Log.SuccessCtx(c) +} diff --git a/api/analytics/routes.go b/api/analytics/routes.go index 0b10346a..04c5e6ce 100644 --- a/api/analytics/routes.go +++ b/api/analytics/routes.go @@ -11,4 +11,8 @@ func AnalyticsRoutes(r *gin.RouterGroup) { r.GET("/registrations", mw.Auth, mw.CheckAdmin, GetEventRegistrationAnalytics) // r.GET("/people", mw.Auth, mw.CheckAdmin, GetPeopleAnalytics) r.GET("/transactions", mw.Auth, mw.CheckAdmin, GetTransactionAnalytics) + + // Hospitality Analytics + r.GET("/hospitality/inside", mw.Auth, mw.CheckHospitality, GetInsideCampusAnalytics) + r.GET("/hospitality/beds", mw.Auth, mw.CheckHospitality, GetLiveBedsAnalytics) } diff --git a/bruno/hospitalityAnalytics/GetInsideCampusAnalytics.bru b/bruno/hospitalityAnalytics/GetInsideCampusAnalytics.bru new file mode 100644 index 00000000..1fe699bc --- /dev/null +++ b/bruno/hospitalityAnalytics/GetInsideCampusAnalytics.bru @@ -0,0 +1,16 @@ +meta { + name: GetInsideCampusAnalytics + type: http + seq: 1 +} + +get { + url: {{baseUrl}}/analytics/hospitality/inside + body: none + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/bruno/hospitalityAnalytics/GetLiveBedsAnalytics.bru b/bruno/hospitalityAnalytics/GetLiveBedsAnalytics.bru new file mode 100644 index 00000000..eb8bf045 --- /dev/null +++ b/bruno/hospitalityAnalytics/GetLiveBedsAnalytics.bru @@ -0,0 +1,16 @@ +meta { + name: GetLiveBedsAnalytics + type: http + seq: 2 +} + +get { + url: {{baseUrl}}/analytics/hospitality/beds + body: none + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/bruno/hospitalityAnalytics/folder.bru b/bruno/hospitalityAnalytics/folder.bru new file mode 100644 index 00000000..2e244677 --- /dev/null +++ b/bruno/hospitalityAnalytics/folder.bru @@ -0,0 +1,8 @@ +meta { + name: hospitalityAnalytics + seq: 18 +} + +auth { + mode: inherit +} diff --git a/db/gen/annalytics-for-accommodation-panel.sql.go b/db/gen/annalytics-for-accommodation-panel.sql.go new file mode 100644 index 00000000..c2e80978 --- /dev/null +++ b/db/gen/annalytics-for-accommodation-panel.sql.go @@ -0,0 +1,86 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: annalytics-for-accommodation-panel.sql + +package db + +import ( + "context" + "encoding/json" + + "github.com/jackc/pgx/v5/pgtype" +) + +const getInsideCampusAnalyticsQuery = `-- name: GetInsideCampusAnalyticsQuery :many +SELECT + logged_at::date AS date, + jsonb_build_object( + 'IN', COUNT(*) FILTER (WHERE direction = 'IN'), + 'OUT', COUNT(*) FILTER (WHERE direction = 'OUT'), + 'CURRENTLY_INSIDE', COUNT(*) FILTER (WHERE direction = 'IN') - COUNT(*) FILTER (WHERE direction = 'OUT') + ) AS counts +FROM gate_management +GROUP BY date +ORDER BY date +` + +type GetInsideCampusAnalyticsQueryRow struct { + Date pgtype.Date `json:"date"` + Counts json.RawMessage `json:"counts"` +} + +func (q *Queries) GetInsideCampusAnalyticsQuery(ctx context.Context, db DBTX) ([]GetInsideCampusAnalyticsQueryRow, error) { + rows, err := db.Query(ctx, getInsideCampusAnalyticsQuery) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetInsideCampusAnalyticsQueryRow + for rows.Next() { + var i GetInsideCampusAnalyticsQueryRow + if err := rows.Scan(&i.Date, &i.Counts); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getLiveBedsAnalyticsQuery = `-- name: GetLiveBedsAnalyticsQuery :many +SELECT jsonb_build_object( + 'hostels', jsonb_agg( + jsonb_build_object( + 'id', id, + 'hostel_name', hostel_name, + 'room_count', room_count, + 'room_filled', room_filled + ) + ), + 'total_beds_filled', SUM(room_filled) +) +FROM hostel_metadata +` + +func (q *Queries) GetLiveBedsAnalyticsQuery(ctx context.Context, db DBTX) ([]json.RawMessage, error) { + rows, err := db.Query(ctx, getLiveBedsAnalyticsQuery) + if err != nil { + return nil, err + } + defer rows.Close() + var items []json.RawMessage + for rows.Next() { + var jsonb_build_object json.RawMessage + if err := rows.Scan(&jsonb_build_object); err != nil { + return nil, err + } + items = append(items, jsonb_build_object) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/db/queries/annalytics-for-accommodation-panel.sql b/db/queries/annalytics-for-accommodation-panel.sql new file mode 100644 index 00000000..b0432005 --- /dev/null +++ b/db/queries/annalytics-for-accommodation-panel.sql @@ -0,0 +1,26 @@ +-- name: GetInsideCampusAnalyticsQuery :many +SELECT + logged_at::date AS date, + jsonb_build_object( + 'IN', COUNT(*) FILTER (WHERE direction = 'IN'), + 'OUT', COUNT(*) FILTER (WHERE direction = 'OUT'), + 'CURRENTLY_INSIDE', COUNT(*) FILTER (WHERE direction = 'IN') - COUNT(*) FILTER (WHERE direction = 'OUT') + ) AS counts +FROM gate_management +GROUP BY date +ORDER BY date; + +-- name: GetLiveBedsAnalyticsQuery :many +SELECT jsonb_build_object( + 'hostels', jsonb_agg( + jsonb_build_object( + 'id', id, + 'hostel_name', hostel_name, + 'room_count', room_count, + 'room_filled', room_filled + ) + ), + 'total_beds_filled', SUM(room_filled) +) +FROM hostel_metadata; +