Skip to content

Commit

Permalink
Merge pull request #35 from ProjectBARO/feat/analysis-rank/#34
Browse files Browse the repository at this point in the history
[close #34] 주간 종합 점수 순위 기능 구현
  • Loading branch information
YehyeokBang authored Feb 3, 2024
2 parents 76d8417 + 8532d0e commit 94f98e2
Show file tree
Hide file tree
Showing 11 changed files with 541 additions and 4 deletions.
26 changes: 26 additions & 0 deletions app/report/controllers/report_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,32 @@ func (controller *ReportController) GetAnalysisSummary(c *gin.Context) {
})
}

// @Tags Reports
// @Summary 내가 상위 몇 %인지 조회 (나이대 및 성별에 따른)
// @Description 로그인한 사용자의 자세 추정 결과를 통해 해당 사용자가 상위 몇 %인지 조회합니다.
// @Accept json
// @Produce json
// @Success 200 {object} global.Response
// @Failure 400 {object} global.Response
// @Security Bearer
// @Router /analysis/rank [get]
func (controller *ReportController) GetAnalysisRankAtAgeAndGender(c *gin.Context) {
response, err := controller.ReportService.FindRankAtAgeAndGender(c)
if err != nil {
c.JSON(400, global.Response{
Status: 400,
Message: err.Error(),
})
return
}

c.JSON(200, global.Response{
Status: 200,
Message: "success",
Data: response,
})
}

// @Tags Reports
// @Summary 자세 추정 결과 전체 조회 (테스트용)
// @Description 자세 추정 결과를 조회합니다.
Expand Down
65 changes: 65 additions & 0 deletions app/report/repositories/report_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package repositories

import (
"gdsc/baro/app/report/models"
"gdsc/baro/app/report/types"
users "gdsc/baro/app/user/models"
"time"

"gorm.io/gorm"
)
Expand All @@ -12,6 +15,7 @@ type ReportRepositoryInterface interface {
FindById(id uint) (models.Report, error)
FindByYearAndMonth(userID uint, month string) ([]models.Report, error)
FindAll() ([]models.Report, error)
FindRankAtAgeAndGender(user *users.User, start, end time.Time) (types.ResponseRank, error)
}

type ReportRepository struct {
Expand Down Expand Up @@ -54,3 +58,64 @@ func (repo *ReportRepository) FindAll() ([]models.Report, error) {
result := repo.DB.Find(&reports)
return reports, result.Error
}

func (repo *ReportRepository) FindRankAtAgeAndGender(user *users.User, start, end time.Time) (types.ResponseRank, error) {
var userAvgScore float64
var totalUsers, rank int64
ageGroup := user.Age / 10 * 10

// calculate average score for the user
err := repo.DB.Table("reports").
Select("avg(score)").
Joins("inner join users on users.id = reports.user_id").
Where("reports.user_id = ?", user.ID).
Where("reports.created_at BETWEEN ? AND ?", start, end).
Scan(&userAvgScore).Error

if err != nil {
return types.ResponseRank{}, err
}

// calculate total users in the same age group and gender
err = repo.DB.Table("users").
Where("age >= ? AND age < ?", ageGroup, ageGroup+10).
Where("gender = ?", user.Gender).
Count(&totalUsers).Error

if err != nil {
return types.ResponseRank{}, err
}

// calculate rank
sql := `
SELECT COUNT(*) as rank_count
FROM (
SELECT reports.user_id, AVG(score) as average_score
FROM reports
INNER JOIN users on users.id = reports.user_id
WHERE users.age >= ? AND users.age < ?
AND users.gender = ?
AND reports.created_at BETWEEN ? AND ?
GROUP BY reports.user_id
) as subquery
WHERE average_score > ?
`

err = repo.DB.Raw(sql, ageGroup, ageGroup+10, user.Gender, start, end, userAvgScore).Scan(&rank).Error
if err != nil {
return types.ResponseRank{}, err
}

rank = totalUsers - rank

normalRatio := float64(rank) / float64(totalUsers) * 100

return types.ResponseRank{
UserID: user.ID,
Nickname: user.Nickname,
Age: user.Age,
Gender: user.Gender,
NormalRatio: normalRatio,
AverageScore: userAvgScore,
}, nil
}
244 changes: 244 additions & 0 deletions app/report/repositories/report_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package repositories_test
import (
"gdsc/baro/app/report/models"
"gdsc/baro/app/report/repositories"
usermodel "gdsc/baro/app/user/models"
"testing"
"time"

Expand Down Expand Up @@ -594,3 +595,246 @@ func TestReportRepository_FindAll_Error(t *testing.T) {
// Check that the expectations were met
assert.NoError(t, mock.ExpectationsWereMet())
}

func TestReportRepository_FindRankAtAgeAndGender(t *testing.T) {
// Create mock DB
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("Error creating mock DB: %v", err)
}

// Set up expectations for the mock DB (ex: SELECT VERSION())
mock.ExpectQuery("SELECT VERSION()").
WillReturnRows(sqlmock.NewRows([]string{"VERSION"}).
AddRow("8.0.0"))

// Create gorm.DB
gormDB, err := gorm.Open(mysql.New(mysql.Config{Conn: db}), &gorm.Config{})
if err != nil {
t.Fatalf("Error creating gorm.DB: %v", err)
}

// Create ReportRepository
reportRepository := repositories.NewReportRepository(gormDB)

// Create sample user for the test
user := usermodel.User{
ID: 1,
Name: "test",
Nickname: "test",
Email: "[email protected]",
Age: 20,
Gender: "male",
}

start := time.Now().AddDate(0, 0, -30)
end := time.Now()

ageGroup := 20
userAvgScore := 80.0

// Set up expectations for the mock DB to return the sample report
mock.ExpectQuery("SELECT avg\\(score\\) FROM `reports` inner join users on users.id = reports.user_id WHERE reports.user_id = \\? AND \\(reports.created_at BETWEEN \\? AND \\?\\)").
WithArgs(user.ID, start, end).
WillReturnRows(sqlmock.NewRows([]string{"avg"}).
AddRow("80.0"))

// Set up expectations for the mock DB to return total users
mock.ExpectQuery("SELECT count\\(\\*\\) FROM `users` WHERE \\(age >= \\? AND age < \\?\\) AND gender = \\?").
WithArgs(ageGroup, ageGroup+10, user.Gender).
WillReturnRows(sqlmock.NewRows([]string{"count"}).
AddRow("100"))

// Set up expectations for the mock DB to return rank
mock.ExpectQuery("SELECT COUNT\\(\\*\\) as rank_count FROM \\(\\s*SELECT reports.user_id, AVG\\(score\\) as average_score FROM reports INNER JOIN users\\s+on\\s+users.id = reports.user_id WHERE users.age >= \\? AND users.age < \\? AND users.gender = \\? AND reports.created_at BETWEEN \\? AND \\? GROUP BY reports.user_id\\s*\\) as subquery WHERE average_score > \\?").
WithArgs(ageGroup, ageGroup+10, user.Gender, start, end, userAvgScore).
WillReturnRows(sqlmock.NewRows([]string{"rank_count"}).
AddRow("20"))

// Call the method under test
responseRank, err := reportRepository.FindRankAtAgeAndGender(&user, start, end)
if err != nil {
t.Fatalf("Error finding rank: %v", err)
}

// Check that the expectations were met
assert.NoError(t, mock.ExpectationsWereMet())

// Check the result
assert.Equal(t, user.ID, responseRank.UserID)
assert.Equal(t, user.Nickname, responseRank.Nickname)
assert.Equal(t, user.Age, responseRank.Age)
assert.Equal(t, user.Gender, responseRank.Gender)
assert.Equal(t, 80.0, responseRank.NormalRatio)
assert.Equal(t, 80.0, responseRank.AverageScore)
}

func TestReportRepository_FindRankAtAgeAndGender_Error(t *testing.T) {
// Create mock DB
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("Error creating mock DB: %v", err)
}

// Set up expectations for the mock DB (ex: SELECT VERSION())
mock.ExpectQuery("SELECT VERSION()").
WillReturnRows(sqlmock.NewRows([]string{"VERSION"}).
AddRow("8.0.0"))

// Create gorm.DB
gormDB, err := gorm.Open(mysql.New(mysql.Config{Conn: db}), &gorm.Config{})
if err != nil {
t.Fatalf("Error creating gorm.DB: %v", err)
}

// Create ReportRepository
reportRepository := repositories.NewReportRepository(gormDB)

// Create sample user for the test
user := usermodel.User{
ID: 1,
Name: "test",
Nickname: "test",
Email: "[email protected]",
Age: 20,
Gender: "male",
}

start := time.Now().AddDate(0, 0, -30)
end := time.Now()

// Set up expectations for the mock DB to return the sample report
mock.ExpectQuery("SELECT avg\\(score\\) FROM `reports` inner join users on users.id = reports.user_id WHERE reports.user_id = \\? AND \\(reports.created_at BETWEEN \\? AND \\?\\)").
WithArgs(user.ID, start, end).
WillReturnError(err)

// Call the method under test
_, err = reportRepository.FindRankAtAgeAndGender(&user, start, end)
if err == nil {
t.Fatalf("Error finding rank: %v", err)
}

// Check that the expectations were met
assert.NoError(t, mock.ExpectationsWereMet())
}

func TestReportRepository_FindRankAtAgeAndGender_Error2(t *testing.T) {
// Create mock DB
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("Error creating mock DB: %v", err)
}

// Set up expectations for the mock DB (ex: SELECT VERSION())
mock.ExpectQuery("SELECT VERSION()").
WillReturnRows(sqlmock.NewRows([]string{"VERSION"}).
AddRow("8.0.0"))

// Create gorm.DB
gormDB, err := gorm.Open(mysql.New(mysql.Config{Conn: db}), &gorm.Config{})
if err != nil {
t.Fatalf("Error creating gorm.DB: %v", err)
}

// Create ReportRepository
reportRepository := repositories.NewReportRepository(gormDB)

// Create sample user for the test
user := usermodel.User{
ID: 1,
Name: "test",
Nickname: "test",
Email: "[email protected]",
Age: 20,
Gender: "male",
}

start := time.Now().AddDate(0, 0, -30)
end := time.Now()

ageGroup := 20

// Set up expectations for the mock DB to return the sample report
mock.ExpectQuery("SELECT avg\\(score\\) FROM `reports` inner join users on users.id = reports.user_id WHERE reports.user_id = \\? AND \\(reports.created_at BETWEEN \\? AND \\?\\)").
WithArgs(user.ID, start, end).
WillReturnRows(sqlmock.NewRows([]string{"avg"}).
AddRow("80.0"))

// Set up expectations for the mock DB to return total users
mock.ExpectQuery("SELECT count\\(\\*\\) FROM `users` WHERE \\(age >= \\? AND age < \\?\\) AND gender = \\?").
WithArgs(ageGroup, ageGroup+10, user.Gender).
WillReturnError(err)

// Call the method under test
_, err = reportRepository.FindRankAtAgeAndGender(&user, start, end)
if err == nil {
t.Fatalf("Error finding rank: %v", err)
}

// Check that the expectations were met
assert.NoError(t, mock.ExpectationsWereMet())
}

func TestReportRepository_FindRankAtAgeAndGender_Error3(t *testing.T) {
// Create mock DB
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("Error creating mock DB: %v", err)
}

// Set up expectations for the mock DB (ex: SELECT VERSION())
mock.ExpectQuery("SELECT VERSION()").
WillReturnRows(sqlmock.NewRows([]string{"VERSION"}).
AddRow("8.0.0"))

// Create gorm.DB
gormDB, err := gorm.Open(mysql.New(mysql.Config{Conn: db}), &gorm.Config{})
if err != nil {
t.Fatalf("Error creating gorm.DB: %v", err)
}

// Create ReportRepository
reportRepository := repositories.NewReportRepository(gormDB)

// Create sample user for the test
user := usermodel.User{
ID: 1,
Name: "test",
Nickname: "test",
Email: "[email protected]",
Age: 20,
Gender: "male",
}

start := time.Now().AddDate(0, 0, -30)
end := time.Now()

ageGroup := 20
userAvgScore := 80.0

// Set up expectations for the mock DB to return the sample report
mock.ExpectQuery("SELECT avg\\(score\\) FROM `reports` inner join users on users.id = reports.user_id WHERE reports.user_id = \\? AND \\(reports.created_at BETWEEN \\? AND \\?\\)").
WithArgs(user.ID, start, end).
WillReturnRows(sqlmock.NewRows([]string{"avg"}).
AddRow("80.0"))

// Set up expectations for the mock DB to return total users
mock.ExpectQuery("SELECT count\\(\\*\\) FROM `users` WHERE \\(age >= \\? AND age < \\?\\) AND gender = \\?").
WithArgs(ageGroup, ageGroup+10, user.Gender).
WillReturnRows(sqlmock.NewRows([]string{"count"}).
AddRow("100"))

// Set up expectations for the mock DB to return rank
mock.ExpectQuery("SELECT COUNT\\(\\*\\) as rank_count FROM \\(\\s*SELECT reports.user_id, AVG\\(score\\) as average_score FROM reports INNER JOIN users\\s+on\\s+users.id = reports.user_id WHERE users.age >= \\? AND users.age < \\? AND users.gender = \\? AND reports.created_at BETWEEN \\? AND \\? GROUP BY reports.user_id\\s*\\) as subquery WHERE average_score > \\?").
WithArgs(ageGroup, ageGroup+10, user.Gender, start, end, userAvgScore).
WillReturnError(err)

// Call the method under test
_, err = reportRepository.FindRankAtAgeAndGender(&user, start, end)
if err == nil {
t.Fatalf("Error finding rank: %v", err)
}

// Check that the expectations were met
assert.NoError(t, mock.ExpectationsWereMet())
}
Loading

0 comments on commit 94f98e2

Please sign in to comment.