Skip to content

Commit

Permalink
Merge pull request #99 from oj-lab/zztrans/rank
Browse files Browse the repository at this point in the history
Rank design and implement, in progress.
  • Loading branch information
slhmy authored Sep 4, 2024
2 parents 4fce239 + 34b5f46 commit 116ffa8
Show file tree
Hide file tree
Showing 17 changed files with 733 additions and 5 deletions.
21 changes: 17 additions & 4 deletions cmd/clean/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import (
"context"

"github.com/minio/minio-go/v7"
casbin_agent "github.com/oj-lab/oj-lab-platform/modules/agent/casbin"

judge_model "github.com/oj-lab/oj-lab-platform/models/judge"
problem_model "github.com/oj-lab/oj-lab-platform/models/problem"
user_model "github.com/oj-lab/oj-lab-platform/models/user"
casbin_agent "github.com/oj-lab/oj-lab-platform/modules/agent/casbin"
gorm_agent "github.com/oj-lab/oj-lab-platform/modules/agent/gorm"
minio_agent "github.com/oj-lab/oj-lab-platform/modules/agent/minio"
redis_agent "github.com/oj-lab/oj-lab-platform/modules/agent/redis"

log_module "github.com/oj-lab/oj-lab-platform/modules/log"
)
Expand Down Expand Up @@ -44,6 +44,16 @@ func removeMinioObjects() {
log_module.AppLogger().Info("Remove Minio Objects success")
}

func clearRedis() {
ctx := context.Background()
redis_agent := redis_agent.GetDefaultRedisClient()
err := redis_agent.FlushDB(ctx).Err()
if err != nil {
log_module.AppLogger().WithError(err).Error("Failed to clear redis")
}

log_module.AppLogger().Info("Clear Redis success")
}
func clearDB() {
db := gorm_agent.GetDefaultDB()

Expand All @@ -53,6 +63,8 @@ func clearDB() {
&problem_model.ProblemTag{},
&judge_model.Judge{},
&judge_model.JudgeResult{},
&judge_model.JudgeScoreCache{},
&judge_model.JudgeRankCache{},
"problem_problem_tags",
"casbin_rule",
)
Expand All @@ -61,12 +73,13 @@ func clearDB() {
panic("failed to drop tables")
}

log_module.AppLogger().Info("Clear db success")

log_module.AppLogger().Info("Clear DB success")
}

func main() {
removeMinioObjects()
clearCasbin()
clearRedis()
clearDB()

println("data clean success")
Expand Down
4 changes: 4 additions & 0 deletions cmd/init/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ func initDB() {
&problem_model.Problem{},
&judge_model.Judge{},
&judge_model.JudgeResult{},
&judge_model.JudgeScoreCache{},
&judge_model.JudgeRankCache{},
)
if err != nil {
panic("failed to migrate database")
Expand All @@ -36,8 +38,10 @@ func initDB() {
Account: "anonymous",
Password: func() *string { s := ""; return &s }(),
})

if err != nil {
panic(fmt.Sprintf("failed to create anonymous user: %v", err))
}

log_module.AppLogger().Info("migrate tables ans users success")
}
6 changes: 6 additions & 0 deletions cmd/web_server/handler/judge_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,10 @@ func postReportJudgeResult(ginCtx *gin.Context) {
gin_utils.NewInternalError(ginCtx, err.Error())
return
}

err = judge_service.UpsertJudgeCache(ginCtx, judgeUID, verdict)
if err != nil {
gin_utils.NewInternalError(ginCtx, err.Error())
return
}
}
60 changes: 60 additions & 0 deletions cmd/web_server/handler/rank.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package handler

import (
"github.com/gin-gonic/gin"
gin_utils "github.com/oj-lab/oj-lab-platform/modules/utils/gin"
judge_service "github.com/oj-lab/oj-lab-platform/services/judge"
)

func SetupRankRouter(baseRoute *gin.RouterGroup) {
g := baseRoute.Group("/rank")
{
g.GET("", getRankList)
// g.GET("/:account", getUserRank)
}
}

// getUserRank
//
// @Router /rank/{account} [get]
// @Summary Get a user rank
// @Description Get a rank for user
// @Tags problem
// @Accept json
// @Success 200
// func getUserRank(ginCtx *gin.Context) {

// }

// getRankList
//
// @Router /rank [get]
// @Summary Get rank list
// @Description Get rank list
// @Tags rank
// @Accept json
// @Success 200
func getRankList(ginCtx *gin.Context) {
limit, err := gin_utils.QueryInt(ginCtx, "limit", 100)
if err != nil {
gin_utils.NewInvalidParamError(ginCtx, "limit", err.Error())
return
}
offset, err := gin_utils.QueryInt(ginCtx, "offset", 0)
if err != nil {
gin_utils.NewInvalidParamError(ginCtx, "offset", err.Error())
return
}

rankInfoList, err := judge_service.GetRankList(
ginCtx,
nil,
&limit, &offset,
)
if err != nil {
gin_utils.NewInternalError(ginCtx, err.Error())
return
}

ginCtx.JSON(200, rankInfoList)
}
1 change: 1 addition & 0 deletions cmd/web_server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func main() {
handler.SetupJudgeRouter(apiRouter)
handler.SetupJudgeTaskRouter(apiRouter)
handler.SetupJudgeResultRouter(apiRouter)
handler.SetupRankRouter(apiRouter)

err := r.Run(fmt.Sprintf(":%d", servicePort))
if err != nil {
Expand Down
17 changes: 17 additions & 0 deletions models/judge/judge_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ func CreateJudge(tx *gorm.DB, judge Judge) (*Judge, error) {
return &judge, tx.Create(&judge).Error
}

// include self, only count if when accept && accept time < SolvedTime
func GetBeforeSubmission(tx *gorm.DB, judge Judge) (int, error) {
var count int64
err := tx.Model(&Judge{}).
Where("create_at < ?", judge.CreateAt).
Where("status = ?", JudgeStatusFinished).
Count(&count).Error

if err != nil {
return 0, err
}
return int(count + 1), err
}

func GetJudge(tx *gorm.DB, uid uuid.UUID) (*Judge, error) {
judge := Judge{}
err := tx.Model(&Judge{}).
Expand Down Expand Up @@ -140,6 +154,9 @@ func UpdateJudge(tx *gorm.DB, judge Judge) error {
if judge.Verdict != "" {
updatingJudge.Verdict = judge.Verdict
}
if judge.MetaFields.CreateAt != nil {
updatingJudge.MetaFields = judge.MetaFields
}

return tx.Model(&updatingJudge).Updates(updatingJudge).Error
}
14 changes: 14 additions & 0 deletions models/judge/judge_rank.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package judge_model

import "github.com/oj-lab/oj-lab-platform/models"

type JudgeRank struct {
Rank int
AvatarURL string
Name string
Points int
TotalSubmissions int
AcceptRate float32
}

var RankInfoSelection = append([]string{"user_account", "points", "total_submissions"}, models.MetaFieldsSelection...)
23 changes: 23 additions & 0 deletions models/judge/judge_rank_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package judge_model

import (
"github.com/oj-lab/oj-lab-platform/models"
user_model "github.com/oj-lab/oj-lab-platform/models/user"
)

// user contest summary rank info
type JudgeRankCache struct {
models.MetaFields
UserAccount string `json:"userAccount" gorm:"primaryKey"`
User user_model.User `json:"user"`
Points int `json:"points"`
TotalSubmissions int `json:"totalSubmissions"`
}

func NewJudgeRankCache(userAccount string) JudgeRankCache {
return JudgeRankCache{
UserAccount: userAccount,
Points: 0,
TotalSubmissions: 0,
}
}
43 changes: 43 additions & 0 deletions models/judge/judge_rank_cache_db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package judge_model

import (
"errors"

"github.com/oj-lab/oj-lab-platform/models"
"gorm.io/gorm"
)

func CreateJudgeRankCache(tx *gorm.DB, rankCache JudgeRankCache) (*JudgeRankCache, error) {
rankCache.MetaFields = models.NewMetaFields()
return &rankCache, tx.Create(&rankCache).Error
}

func DeleteJudgeRankCache(tx *gorm.DB, userAccount string) error {
var judgeRankCache JudgeRankCache
if err := tx.Where("user_account = ?", userAccount).First(&judgeRankCache).Error; err != nil {
return err
}
return tx.Delete(&judgeRankCache).Error
}

func GetJudgeRankCache(tx *gorm.DB, userAccount string) (*JudgeRankCache, error) {
rankCache := JudgeRankCache{}
err := tx.Model(&JudgeRankCache{}).
Where("user_account = ?", userAccount).
First(&rankCache).Error
if err != nil {
return nil, err
}
return &rankCache, nil
}

func UpdateJudgeRankCache(tx *gorm.DB, rankCache JudgeRankCache) error {
return tx.Model(&rankCache).Updates(rankCache).Error
}

func (rankCache *JudgeRankCache) BeforeSave(tx *gorm.DB) (err error) {
if rankCache.Points > rankCache.TotalSubmissions {
return errors.New("TotalSubmissions must >= Points")
}
return nil
}
43 changes: 43 additions & 0 deletions models/judge/judge_rank_db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package judge_model

import "gorm.io/gorm"

type GetRankOptions struct {
Selection []string
UserAccount *string
Offset *int
Limit *int
}

func buildGetRankCacheTXByOptions(tx *gorm.DB, options GetRankOptions, isCount bool) *gorm.DB {
tx = tx.Model(&JudgeRankCache{})
if len(options.Selection) > 0 {
tx = tx.Select(options.Selection)
}
if options.UserAccount != nil {
tx = tx.Where("user_account = ?", *options.UserAccount)
}

tx = tx.Distinct().Preload("User")
if !isCount {
if options.Offset != nil {
tx = tx.Offset(*options.Offset)
}
if options.Limit != nil {
tx = tx.Limit(*options.Limit)
}
}

tx = tx.Order("points DESC, total_submissions ASC")
return tx
}

func GetRankCacheListByOptions(tx *gorm.DB, options GetRankOptions) ([]JudgeRankCache, error) {
rankInfoList := []JudgeRankCache{}
tx = buildGetRankCacheTXByOptions(tx, options, false)
err := tx.Find(&rankInfoList).Error
if err != nil {
return nil, err
}
return rankInfoList, nil
}
30 changes: 30 additions & 0 deletions models/judge/judge_score_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package judge_model

import (
"time"

"github.com/oj-lab/oj-lab-platform/models"
problem_model "github.com/oj-lab/oj-lab-platform/models/problem"
user_model "github.com/oj-lab/oj-lab-platform/models/user"
)

// user contest problem summary score info
type JudgeScoreCache struct {
models.MetaFields
UserAccount string `json:"userAccount" gorm:"primaryKey"`
User user_model.User `json:"user"`
ProblemSlug string `json:"problemSlug" gorm:"primaryKey"`
Problem problem_model.Problem `json:"problem"`
SubmissionCount int `json:"submissionCount" gorm:"default:0"` // judge create time < solvetime will be counted
IsAccepted bool `json:"isAccepted" gorm:"default:false"`
SolveTime *time.Time `json:"solveAt" gorm:"default:null"` // ac time < solveTime, update submissionCount
}

func NewJudgeScoreCache(userAccount string, problemSlug string) JudgeScoreCache {
return JudgeScoreCache{
UserAccount: userAccount,
ProblemSlug: problemSlug,
SubmissionCount: 0,
IsAccepted: false,
}
}
27 changes: 27 additions & 0 deletions models/judge/judge_score_cache_db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package judge_model

import (
"github.com/oj-lab/oj-lab-platform/models"
"gorm.io/gorm"
)

func CreateJudgeScoreCache(tx *gorm.DB, scoreCache JudgeScoreCache) (*JudgeScoreCache, error) {
scoreCache.MetaFields = models.NewMetaFields()
return &scoreCache, tx.Create(&scoreCache).Error
}

func GetJudgeScoreCache(tx *gorm.DB, userAccount string, problemSlug string) (*JudgeScoreCache, error) {
scoreCache := JudgeScoreCache{}
err := tx.Model(&JudgeScoreCache{}).
Where("user_account = ?", userAccount).
Where("problem_slug = ?", problemSlug).
First(&scoreCache).Error
if err != nil {
return nil, err
}
return &scoreCache, nil
}

func UpdateJudgeScoreCache(tx *gorm.DB, scoreCache JudgeScoreCache) error {
return tx.Model(&scoreCache).Updates(scoreCache).Error
}
Loading

0 comments on commit 116ffa8

Please sign in to comment.