From b31bc76a24e63537535431610c831f779e330bf1 Mon Sep 17 00:00:00 2001 From: nochill Date: Wed, 4 Oct 2023 19:51:29 +0700 Subject: [PATCH] add user profile stats --- api/server.go | 3 +- api/user.go | 37 +++++++++++++++++++++++ db/mock/store.go | 15 ++++++++++ db/sqlc/store.go | 1 + db/sqlc/users.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 130 insertions(+), 3 deletions(-) diff --git a/api/server.go b/api/server.go index 80fd346..296f42f 100644 --- a/api/server.go +++ b/api/server.go @@ -39,7 +39,7 @@ func (server *Server) getRoutes() { router.Use(cors.New(cors.Config{ AllowOrigins: []string{"http://localhost:5173"}, AllowCredentials: true, - AllowHeaders: []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With"}, + AllowHeaders: []string{"Content-Type", "Content-Length", "Accept-Encoding", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With", "X-XSRF-TOKEN"}, AllowMethods: []string{"POST", "PUT", "GET", "DELETE", "PATCH"}, })) @@ -68,6 +68,7 @@ func (server *Server) getRoutes() { authRoutes := router.Use(authMiddleware(server.TokenMaker)) authRoutes.POST("/review/location", server.createReview) authRoutes.GET("/user/review/location/:location_id", server.getUserReviewByLocation) + authRoutes.GET("/user/profile", server.getUserStats) server.Router = router } diff --git a/api/user.go b/api/user.go index 9c22dec..8a9162d 100644 --- a/api/user.go +++ b/api/user.go @@ -2,11 +2,13 @@ package api import ( "database/sql" + "encoding/json" "net/http" "time" db "git.nochill.in/nochill/hiling_go/db/sqlc" "git.nochill.in/nochill/hiling_go/util" + "git.nochill.in/nochill/hiling_go/util/token" "github.com/gin-gonic/gin" "github.com/lib/pq" ) @@ -124,6 +126,41 @@ func (server *Server) createUser(ctx *gin.Context) { ctx.JSON(http.StatusOK, res) } +func (server *Server) getUserStats(ctx *gin.Context) { + var scoreDistribution []map[string]int8 + authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload) + + userStats, err := server.Store.GetUserStats(ctx, int32(authPayload.UserID)) + + if err != nil { + ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get user stats")) + return + } + + err = json.Unmarshal(userStats.ScoresDistribution, &scoreDistribution) + + if err != nil { + ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong whilet try to parse score distribution")) + return + } + + var userReviews []map[string]any + if userStats.Reviews != nil { + err = json.Unmarshal(userStats.Reviews, &userReviews) + if err != nil { + ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something wrong while try to parse user reviwes stats")) + return + } + } else { + userReviews = make([]map[string]any, 0) + } + ctx.JSON(http.StatusOK, gin.H{ + "reviews": userReviews, + "scores_distribution": scoreDistribution, + "user_stats": userStats, + }) +} + func (server *Server) login(ctx *gin.Context) { var req createUserRequest diff --git a/db/mock/store.go b/db/mock/store.go index d140ce9..8f7c32e 100644 --- a/db/mock/store.go +++ b/db/mock/store.go @@ -362,6 +362,21 @@ func (mr *MockStoreMockRecorder) GetUserReviewByLocation(arg0, arg1 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserReviewByLocation", reflect.TypeOf((*MockStore)(nil).GetUserReviewByLocation), arg0, arg1) } +// GetUserStats mocks base method. +func (m *MockStore) GetUserStats(arg0 context.Context, arg1 int32) (db.GetUserStatsRow, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUserStats", arg0, arg1) + ret0, _ := ret[0].(db.GetUserStatsRow) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUserStats indicates an expected call of GetUserStats. +func (mr *MockStoreMockRecorder) GetUserStats(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserStats", reflect.TypeOf((*MockStore)(nil).GetUserStats), arg0, arg1) +} + // RemoveFollowUser mocks base method. func (m *MockStore) RemoveFollowUser(arg0 context.Context, arg1 db.RemoveFollowUserParams) error { m.ctrl.T.Helper() diff --git a/db/sqlc/store.go b/db/sqlc/store.go index c6b689e..a43317f 100644 --- a/db/sqlc/store.go +++ b/db/sqlc/store.go @@ -14,6 +14,7 @@ type Store interface { GetImagesByLocation(ctx context.Context, arg GetImagesByLocationParams) ([]GetImagesByLocationRow, error) GetLocation(ctx context.Context, location_id int32) (GetLocationRow, error) GetUser(ctx context.Context, username string) (GetUserRow, error) + GetUserStats(ctx context.Context, user_id int32) (GetUserStatsRow, error) CreateReview(ctx context.Context, arg CreateReviewParams) (Review, error) GetListLocationReviews(ctx context.Context, arg GetListLocationReviewsParams) ([]GetListLocationReviewsRow, error) CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error) diff --git a/db/sqlc/users.go b/db/sqlc/users.go index 8f769f8..7722cb2 100644 --- a/db/sqlc/users.go +++ b/db/sqlc/users.go @@ -3,11 +3,13 @@ package db import ( "context" "database/sql" + "encoding/json" + "fmt" "github.com/sqlc-dev/pqtype" ) -const getUser = `-- name: GetUser :one +const getUserQ = `-- name: GetUser :one SELECT id, COALESCE(email, '') as email, @@ -43,7 +45,7 @@ type GetUserRow struct { } func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, error) { - row := q.db.QueryRowContext(ctx, getUser, username) + row := q.db.QueryRowContext(ctx, getUserQ, username) var i GetUserRow err := row.Scan( &i.ID, @@ -62,3 +64,74 @@ func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, err ) return i, err } + +const getUserStatsQ = ` +SELECT +json_agg(ur.*) AS reviews, +( SELECT COUNT(id) FROM user_follow u WHERE u.followee_id = $1) as followers, +( SELECT COUNT(id) FROM reviews r WHERE r.submitted_by = $1 ) as score_count, +( SELECT + json_agg(r1.*) AS scores_distribution + FROM + ( + SELECT + * + FROM +( SELECT COUNT(id) as "0" FROM reviews r WHERE r.score >= 0 AND r.score <= 9 AND r.submitted_by = $1) as score0, +( SELECT COUNT(id) as "1" FROM reviews r WHERE r.score >= 10 AND r.score <= 19 AND r.submitted_by = $1) as score1, +( SELECT COUNT(id) as "2" FROM reviews r WHERE r.score >= 20 AND r.score <= 29 AND r.submitted_by = $1) as score2, +( SELECT COUNT(id) as "3" FROM reviews r WHERE r.score >= 30 AND r.score <= 39 AND r.submitted_by = $1) as score3, +( SELECT COUNT(id) as "4" FROM reviews r WHERE r.score >= 40 AND r.score <= 49 AND r.submitted_by = $1) as score4, +( SELECT COUNT(id) as "5" FROM reviews r WHERE r.score >= 50 AND r.score <= 59 AND r.submitted_by = $1) as score5, +( SELECT COUNT(id) as "6" FROM reviews r WHERE r.score >= 60 AND r.score <= 69 AND r.submitted_by = $1) as score6, +( SELECT COUNT(id) as "7" FROM reviews r WHERE r.score >= 70 AND r.score <= 79 AND r.submitted_by = $1) as score7, +( SELECT COUNT(id) as "8" FROM reviews r WHERE r.score >= 80 AND r.score <= 89 AND r.submitted_by = $1) as score8, +( SELECT COUNT(id) as "9" FROM reviews r WHERE r.score >= 90 AND r.score <= 99 AND r.submitted_by = $1) as score9, +( SELECT COUNT(id) as "99" FROM reviews r WHERE r.score = 100 AND r.submitted_by = $1) as score10 + ) r1 +) +FROM ( + SELECT + reviews.id, + reviews.comments, + score, + l.name, + p.province_name, + COALESCE(l.thumbnail, '') as thumbnail + FROM + reviews + JOIN locations l on reviews.location_id = l.id + JOIN regencies r on l.regency_id = r.id + JOIN provinces p on r.province_id = p.id + WHERE reviews.submitted_by = $1 + ORDER BY score ASC + LIMIT 10 + ) ur +` + +type GetUserStatsRow struct { + Reviews []byte `json:"-"` + Followers int32 `json:"followers"` + ScoreCount int32 `json:"score_count"` + ScoresDistribution []byte `json:"-"` +} + +func (q *Queries) GetUserStats(ctx context.Context, user_id int32) (GetUserStatsRow, error) { + var i GetUserStatsRow + row := q.db.QueryRowContext(ctx, getUserStatsQ, user_id) + + err := row.Scan( + &i.Reviews, + &i.Followers, + &i.ScoreCount, + &i.ScoresDistribution, + ) + + var r []map[string]any + + err = json.Unmarshal(i.ScoresDistribution, &r) + fmt.Println(r) + + return i, err + +}