Skip to content

feature/update_dyanamic_engagers #56

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions internal/api/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,24 @@ func listUsersHandler(userSvc user.Service) http.HandlerFunc {

func getActiveUserListHandler(userSvc user.Service) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {

ctx := req.Context()

quarterStr := req.URL.Query().Get("quarter")
yearStr := req.URL.Query().Get("year")

quarter, err := strconv.Atoi(quarterStr)
if err != nil || quarter < 1 || quarter > 4 {
http.Error(rw, "Invalid quarter", http.StatusBadRequest)
return
}
year, err := strconv.Atoi(yearStr)
if err != nil || year < 2024 {
http.Error(rw, "Invalid year", http.StatusBadRequest)
return
}

log.Info(ctx, "getActiveUserListHandler: req: ", req)
resp, err := userSvc.GetActiveUserList(req.Context())
resp, err := userSvc.GetActiveUserList(ctx, quarter, year)
if err != nil {
dto.ErrorRepsonse(rw, err)
return
Expand All @@ -241,6 +255,7 @@ func getActiveUserListHandler(userSvc user.Service) http.HandlerFunc {
dto.SuccessRepsonse(rw, http.StatusOK, "Active Users list", resp)
}
}

func getUserByIdHandler(userSvc user.Service) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {

Expand Down
35 changes: 32 additions & 3 deletions internal/app/users/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type Service interface {
ListUsers(ctx context.Context, reqData dto.ListUsersReq) (resp dto.ListUsersResp, err error)
GetUserById(ctx context.Context) (user dto.GetUserByIdResp, err error)
UpdateRewardQuota(ctx context.Context) (err error)
GetActiveUserList(ctx context.Context) ([]dto.ActiveUser, error)
GetActiveUserList(ctx context.Context, quarter int, year int) ([]dto.ActiveUser, error)
GetTop10Users(ctx context.Context) (users []dto.Top10User, err error)
AdminLogin(ctx context.Context, loginReq dto.AdminLoginReq) (resp dto.LoginUserResp, err error)
NotificationByAdmin(ctx context.Context, notificationReq dto.AdminNotificationReq) (err error)
Expand Down Expand Up @@ -483,8 +483,9 @@ func (us *service) GetUserById(ctx context.Context) (user dto.GetUserByIdResp, e
return
}

func (us *service) GetActiveUserList(ctx context.Context) ([]dto.ActiveUser, error) {
activeUserDb, err := us.userRepo.GetActiveUserList(ctx, nil)
func (us *service) GetActiveUserList(ctx context.Context, quarter int, year int) ([]dto.ActiveUser, error) {
quarterStart, quarterEnd := getQuarterRangeUnixTime(quarter, year)
activeUserDb, err := us.userRepo.GetActiveUserList(ctx, nil, quarterStart, quarterEnd)
if err != nil {
logger.Errorf(ctx, "userService: GetActiveUserList: err: %v", err)
return []dto.ActiveUser{}, err
Expand All @@ -496,6 +497,34 @@ func (us *service) GetActiveUserList(ctx context.Context) ([]dto.ActiveUser, err
}
return res, nil
}

func getQuarterRangeUnixTime(quarter int, year int) (start int64, end int64) {
var startMonth, endMonth time.Month
startYear := year
endYear := year
switch quarter {
case 1:
startMonth = time.March
endMonth = time.June
case 2:
startMonth = time.June
endMonth = time.September
case 3:
startMonth = time.September
endMonth = time.December
case 4:
startMonth = time.December
endMonth = time.March
endYear = year + 1 // Q4 ends next year's March
default:
startMonth = time.January
endMonth = time.January
}
startTime := time.Date(startYear, startMonth, 1, 0, 0, 0, 0, time.UTC)
endTime := time.Date(endYear, endMonth, 1, 0, 0, 0, 0, time.UTC)
return startTime.UnixMilli(), endTime.UnixMilli()
}

func (us *service) UpdateRewardQuota(ctx context.Context) error {
err := us.userRepo.UpdateRewardQuota(ctx, nil)
return err
Expand Down
149 changes: 85 additions & 64 deletions internal/repository/postgresdb/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"time"

"github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
Expand Down Expand Up @@ -262,72 +263,92 @@ func (us *userStore) ListUsers(ctx context.Context, reqData dto.ListUsersReq) (r
return
}

func (us *userStore) GetActiveUserList(ctx context.Context, tx repository.Transaction) (activeUsers []repository.ActiveUser, err error) {
func getQuarterRangeUnixTime(quarter int, year int) (start int64, end int64) {
var startMonth, endMonth time.Month
switch quarter {
case 1:
startMonth = time.March
endMonth = time.June
case 2:
startMonth = time.June
endMonth = time.September
case 3:
startMonth = time.September
endMonth = time.December
case 4:
startMonth = time.December
endMonth = time.March
year += 1 // Q4 ends next year's March
default:
startMonth = time.January
endMonth = time.January
}
startTime := time.Date(year, startMonth, 1, 0, 0, 0, 0, time.UTC)
endTime := time.Date(year, endMonth, 1, 0, 0, 0, 0, time.UTC)
return startTime.Unix(), endTime.Unix()
}

func (us *userStore) GetActiveUserList(ctx context.Context, tx repository.Transaction, quarterStart int64, quarterEnd int64) (activeUsers []repository.ActiveUser, err error) {
queryExecutor := us.InitiateQueryExecutor(tx)
afterTime := GetQuarterStartUnixTime()
query := `WITH user_points AS (
SELECT
u.id AS user_id,
COALESCE(received.total_received_appreciations, 0) AS total_received_appreciations,
COALESCE(sent.total_sent_appreciations, 0) AS total_sent_appreciations,
COALESCE(given.total_given_rewards, 0) AS total_given_rewards,
(3 * COALESCE(sent.total_sent_appreciations, 0) + 2 * COALESCE(received.total_received_appreciations, 0) + COALESCE(given.total_given_rewards, 0)) AS active_user_points
FROM
users u
LEFT JOIN
(SELECT receiver AS user_id, COUNT(*) AS total_received_appreciations
FROM appreciations
WHERE
Appreciations.is_valid = true AND appreciations.created_at >=$1
GROUP BY receiver
) AS received ON u.id = received.user_id
LEFT JOIN
(SELECT sender AS user_id, COUNT(*) AS total_sent_appreciations
FROM appreciations
WHERE
Appreciations.is_valid = true AND appreciations.created_at >=$2
GROUP BY sender) AS sent ON u.id = sent.user_id
LEFT JOIN
(SELECT sender AS user_id, COUNT(*) AS total_given_rewards
FROM rewards
WHERE
rewards.created_at >=$3
GROUP BY sender) AS given ON u.id = given.user_id
WHERE
COALESCE(received.total_received_appreciations, 0) > 0 OR
COALESCE(sent.total_sent_appreciations, 0) > 0 OR
COALESCE(given.total_given_rewards, 0) > 0
ORDER BY
active_user_points DESC,
total_sent_appreciations DESC,
total_given_rewards DESC,
total_received_appreciations DESC
)
SELECT
up.user_id,
u.first_name ,
u.last_name ,
u.profile_image_url,
b.name AS badge,
COALESCE(ap.appreciation_points, 0) AS appreciation_points
FROM
user_points up
JOIN
users u ON up.user_id = u.id
LEFT JOIN
(SELECT receiver, SUM(total_reward_points) AS appreciation_points
FROM appreciations
GROUP BY receiver) AS ap ON u.id = ap.receiver
LEFT JOIN
(SELECT ub.user_id, b.name
FROM user_badges ub
JOIN badges b ON ub.badge_id = b.id
WHERE ub.id = (SELECT MAX(id) FROM user_badges WHERE user_id = ub.user_id)) AS b ON u.id = b.user_id
LIMIT 10;
`
logger.Info(ctx, "afterTime: ", afterTime)

rows, err := queryExecutor.Query(query, afterTime, afterTime, afterTime)
SELECT
u.id AS user_id,
COALESCE(received.total_received_appreciations, 0) AS total_received_appreciations,
COALESCE(sent.total_sent_appreciations, 0) AS total_sent_appreciations,
COALESCE(given.total_given_rewards, 0) AS total_given_rewards,
(3 * COALESCE(sent.total_sent_appreciations, 0) + 2 * COALESCE(received.total_received_appreciations, 0) + COALESCE(given.total_given_rewards, 0)) AS active_user_points
FROM
users u
LEFT JOIN
(SELECT receiver AS user_id, COUNT(*) AS total_received_appreciations
FROM appreciations
WHERE appreciations.is_valid = true AND appreciations.created_at >= $1 AND appreciations.created_at < $2
GROUP BY receiver
) AS received ON u.id = received.user_id
LEFT JOIN
(SELECT sender AS user_id, COUNT(*) AS total_sent_appreciations
FROM appreciations
WHERE appreciations.is_valid = true AND appreciations.created_at >= $1 AND appreciations.created_at < $2
GROUP BY sender) AS sent ON u.id = sent.user_id
LEFT JOIN
(SELECT sender AS user_id, COUNT(*) AS total_given_rewards
FROM rewards
WHERE rewards.created_at >= $1 AND rewards.created_at < $2
GROUP BY sender) AS given ON u.id = given.user_id
WHERE
COALESCE(received.total_received_appreciations, 0) > 0 OR
COALESCE(sent.total_sent_appreciations, 0) > 0 OR
COALESCE(given.total_given_rewards, 0) > 0
ORDER BY
active_user_points DESC,
total_sent_appreciations DESC,
total_given_rewards DESC,
total_received_appreciations DESC
)
SELECT
up.user_id,
u.first_name ,
u.last_name ,
u.profile_image_url,
b.name AS badge,
COALESCE(ap.appreciation_points, 0) AS appreciation_points
FROM
user_points up
JOIN
users u ON up.user_id = u.id
LEFT JOIN
(SELECT receiver, SUM(total_reward_points) AS appreciation_points
FROM appreciations
GROUP BY receiver) AS ap ON u.id = ap.receiver
LEFT JOIN
(SELECT ub.user_id, b.name
FROM user_badges ub
JOIN badges b ON ub.badge_id = b.id
WHERE ub.id = (SELECT MAX(id) FROM user_badges WHERE user_id = ub.user_id)) AS b ON u.id = b.user_id
LIMIT 10;`
logger.Info(ctx, "quarterStart: ", quarterStart, ", quarterEnd: ", quarterEnd)

rows, err := queryExecutor.Query(query, quarterStart, quarterEnd)
if err != nil {
logger.Error(ctx, "err: userStore ", err.Error())
return []repository.ActiveUser{}, err
Expand Down
2 changes: 1 addition & 1 deletion internal/repository/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type UserStorer interface {
ListUsers(ctx context.Context, reqData dto.ListUsersReq) (resp []User, count int64, err error)

UpdateRewardQuota(ctx context.Context, tx Transaction) (err error)
GetActiveUserList(ctx context.Context, tx Transaction) (activeUsers []ActiveUser, err error)
GetActiveUserList(ctx context.Context, tx Transaction, quarterStart int64, quarterEnd int64) (activeUsers []ActiveUser, err error)
GetUserById(ctx context.Context, reqData dto.GetUserByIdReq) (user dto.GetUserByIdResp, err error)
GetTop10Users(ctx context.Context, quarterTimestamp int64) (users []Top10Users, err error)
GetGradeById(ctx context.Context, id int64) (grade Grade, err error)
Expand Down