Implement country leaderboards

This commit is contained in:
Morgan Bazalgette
2017-04-11 23:18:02 +02:00
parent 120c027fce
commit 622658f5aa
4 changed files with 100 additions and 49 deletions

View File

@@ -2,6 +2,12 @@ package v1
import (
"fmt"
"strconv"
"strings"
"github.com/jmoiron/sqlx"
redis "gopkg.in/redis.v5"
"zxq.co/ripple/ocl"
"zxq.co/ripple/rippleapi/common"
@@ -28,29 +34,48 @@ SELECT
users_stats.ranked_score_%[1]s, users_stats.total_score_%[1]s, users_stats.playcount_%[1]s,
users_stats.replays_watched_%[1]s, users_stats.total_hits_%[1]s,
users_stats.avg_accuracy_%[1]s, users_stats.pp_%[1]s, leaderboard_%[1]s.position as %[1]s_position
FROM leaderboard_%[1]s
INNER JOIN users ON users.id = leaderboard_%[1]s.user
INNER JOIN users_stats ON users_stats.id = leaderboard_%[1]s.user
%[2]s`
users_stats.avg_accuracy_%[1]s, users_stats.pp_%[1]s
FROM users
INNER JOIN users_stats ON users_stats.id = users.id
WHERE users.id IN (?)
`
// LeaderboardGET gets the leaderboard.
func LeaderboardGET(md common.MethodData) common.CodeMessager {
m := getMode(md.Query("mode"))
w := &common.WhereClause{
Clause: "WHERE " + md.User.OnlyUserPublic(md.HasQuery("see_everything")),
// md.Query.Country
p := common.Int(md.Query("p")) - 1
if p < 0 {
p = 0
}
w.Where("users_stats.country = ?", md.Query("country"))
// Admins may not want to see banned users on the leaderboard.
// This is the default setting. In case they do, they have to activate see_everything.
query := fmt.Sprintf(lbUserQuery, m, w.Clause+
` ORDER BY leaderboard_`+m+`.position `+common.Paginate(md.Query("p"), md.Query("l"), 500))
rows, err := md.DB.Query(query, w.Params...)
l := common.InString(1, md.Query("l"), 500, 50)
key := "ripple:leaderboard:" + m
if md.Query("country") != "" {
key += ":" + md.Query("country")
}
results, err := md.R.ZRevRange(key, int64(p*l), int64(p*l+l-1)).Result()
if err != nil {
md.Err(err)
return Err500
}
var resp leaderboardResponse
resp.Code = 200
if len(results) == 0 {
return resp
}
query := fmt.Sprintf(lbUserQuery+` ORDER BY users_stats.pp_%[1]s DESC, users_stats.ranked_score_%[1]s DESC`, m)
query, params, _ := sqlx.In(query, results)
rows, err := md.DB.Query(query, params...)
if err != nil {
md.Err(err)
return Err500
}
for rows.Next() {
var u leaderboardUser
err := rows.Scan(
@@ -60,15 +85,28 @@ func LeaderboardGET(md common.MethodData) common.CodeMessager {
&u.ChosenMode.RankedScore, &u.ChosenMode.TotalScore, &u.ChosenMode.PlayCount,
&u.ChosenMode.ReplaysWatched, &u.ChosenMode.TotalHits,
&u.ChosenMode.Accuracy, &u.ChosenMode.PP, &u.ChosenMode.GlobalLeaderboardRank,
&u.ChosenMode.Accuracy, &u.ChosenMode.PP,
)
if err != nil {
md.Err(err)
continue
}
u.ChosenMode.Level = ocl.GetLevelPrecise(int64(u.ChosenMode.TotalScore))
if i := leaderboardPosition(md.R, m, u.ID); i != 0 {
u.ChosenMode.GlobalLeaderboardRank = &i
}
if i := countryPosition(md.R, m, u.ID, u.Country); i != 0 {
u.ChosenMode.CountryLeaderboardRank = &i
}
resp.Users = append(resp.Users, u)
}
resp.Code = 200
return resp
}
func leaderboardPosition(r *redis.Client, mode string, user int) int {
return int(r.ZRevRank("ripple:leaderboard:"+mode, strconv.Itoa(user)).Val()) + 1
}
func countryPosition(r *redis.Client, mode string, user int, country string) int {
return int(r.ZRevRank("ripple:leaderboard:"+mode+":"+strings.ToLower(country), strconv.Itoa(user)).Val()) + 1
}

View File

@@ -166,16 +166,24 @@ func UserWhatsTheIDGET(md common.MethodData) common.CodeMessager {
return r
}
var modesToReadable = [...]string{
"std",
"taiko",
"ctb",
"mania",
}
type modeData struct {
RankedScore uint64 `json:"ranked_score"`
TotalScore uint64 `json:"total_score"`
PlayCount int `json:"playcount"`
ReplaysWatched int `json:"replays_watched"`
TotalHits int `json:"total_hits"`
Level float64 `json:"level"`
Accuracy float64 `json:"accuracy"`
PP int `json:"pp"`
GlobalLeaderboardRank *int `json:"global_leaderboard_rank"`
RankedScore uint64 `json:"ranked_score"`
TotalScore uint64 `json:"total_score"`
PlayCount int `json:"playcount"`
ReplaysWatched int `json:"replays_watched"`
TotalHits int `json:"total_hits"`
Level float64 `json:"level"`
Accuracy float64 `json:"accuracy"`
PP int `json:"pp"`
GlobalLeaderboardRank *int `json:"global_leaderboard_rank"`
CountryLeaderboardRank *int `json:"country_leaderboard_rank"`
}
type userFullResponse struct {
common.ResponseBase
@@ -214,33 +222,25 @@ SELECT
users_stats.ranked_score_std, users_stats.total_score_std, users_stats.playcount_std,
users_stats.replays_watched_std, users_stats.total_hits_std,
users_stats.avg_accuracy_std, users_stats.pp_std, leaderboard_std.position as std_position,
users_stats.avg_accuracy_std, users_stats.pp_std,
users_stats.ranked_score_taiko, users_stats.total_score_taiko, users_stats.playcount_taiko,
users_stats.replays_watched_taiko, users_stats.total_hits_taiko,
users_stats.avg_accuracy_taiko, users_stats.pp_taiko, leaderboard_taiko.position as taiko_position,
users_stats.avg_accuracy_taiko, users_stats.pp_taiko,
users_stats.ranked_score_ctb, users_stats.total_score_ctb, users_stats.playcount_ctb,
users_stats.replays_watched_ctb, users_stats.total_hits_ctb,
users_stats.avg_accuracy_ctb, users_stats.pp_ctb, leaderboard_ctb.position as ctb_position,
users_stats.avg_accuracy_ctb, users_stats.pp_ctb,
users_stats.ranked_score_mania, users_stats.total_score_mania, users_stats.playcount_mania,
users_stats.replays_watched_mania, users_stats.total_hits_mania,
users_stats.avg_accuracy_mania, users_stats.pp_mania, leaderboard_mania.position as mania_position,
users_stats.avg_accuracy_mania, users_stats.pp_mania,
users.silence_reason, users.silence_end
FROM users
LEFT JOIN users_stats
ON users.id=users_stats.id
LEFT JOIN leaderboard_std
ON users.id=leaderboard_std.user
LEFT JOIN leaderboard_taiko
ON users.id=leaderboard_taiko.user
LEFT JOIN leaderboard_ctb
ON users.id=leaderboard_ctb.user
LEFT JOIN leaderboard_mania
ON users.id=leaderboard_mania.user
WHERE ` + whereClause + ` AND ` + md.User.OnlyUserPublic(true) + `
LIMIT 1
`
@@ -261,19 +261,19 @@ LIMIT 1
&r.STD.RankedScore, &r.STD.TotalScore, &r.STD.PlayCount,
&r.STD.ReplaysWatched, &r.STD.TotalHits,
&r.STD.Accuracy, &r.STD.PP, &r.STD.GlobalLeaderboardRank,
&r.STD.Accuracy, &r.STD.PP,
&r.Taiko.RankedScore, &r.Taiko.TotalScore, &r.Taiko.PlayCount,
&r.Taiko.ReplaysWatched, &r.Taiko.TotalHits,
&r.Taiko.Accuracy, &r.Taiko.PP, &r.Taiko.GlobalLeaderboardRank,
&r.Taiko.Accuracy, &r.Taiko.PP,
&r.CTB.RankedScore, &r.CTB.TotalScore, &r.CTB.PlayCount,
&r.CTB.ReplaysWatched, &r.CTB.TotalHits,
&r.CTB.Accuracy, &r.CTB.PP, &r.CTB.GlobalLeaderboardRank,
&r.CTB.Accuracy, &r.CTB.PP,
&r.Mania.RankedScore, &r.Mania.TotalScore, &r.Mania.PlayCount,
&r.Mania.ReplaysWatched, &r.Mania.TotalHits,
&r.Mania.Accuracy, &r.Mania.PP, &r.Mania.GlobalLeaderboardRank,
&r.Mania.Accuracy, &r.Mania.PP,
&r.SilenceInfo.Reason, &r.SilenceInfo.End,
)
@@ -290,8 +290,15 @@ LIMIT 1
r.CustomBadge = &b
}
for _, m := range []*modeData{&r.STD, &r.Taiko, &r.CTB, &r.Mania} {
for modeID, m := range [...]*modeData{&r.STD, &r.Taiko, &r.CTB, &r.Mania} {
m.Level = ocl.GetLevelPrecise(int64(m.TotalScore))
if i := leaderboardPosition(md.R, modesToReadable[modeID], r.ID); i != 0 {
m.GlobalLeaderboardRank = &i
}
if i := countryPosition(md.R, modesToReadable[modeID], r.ID, r.Country); i != 0 {
m.CountryLeaderboardRank = &i
}
}
rows, err := md.DB.Query("SELECT b.id, b.name, b.icon FROM user_badges ub "+