1
0
mirror of https://github.com/muety/wakapi.git synced 2023-08-10 21:12:56 +03:00
wakapi/routes/leaderboard.go

145 lines
4.7 KiB
Go
Raw Normal View History

2022-10-03 11:53:27 +03:00
package routes
import (
"fmt"
2022-10-05 22:52:10 +03:00
"github.com/duke-git/lancet/v2/slice"
"github.com/emvi/logbuch"
2022-10-03 11:53:27 +03:00
"github.com/gorilla/mux"
conf "github.com/muety/wakapi/config"
"github.com/muety/wakapi/middlewares"
"github.com/muety/wakapi/models"
"github.com/muety/wakapi/models/view"
"github.com/muety/wakapi/services"
"github.com/muety/wakapi/utils"
2022-10-03 11:53:27 +03:00
"net/http"
"strings"
2022-10-03 11:53:27 +03:00
)
type LeaderboardHandler struct {
config *conf.Config
userService services.IUserService
leaderboardService services.ILeaderboardService
}
var allowedAggregations = map[string]uint8{
"language": models.SummaryLanguage,
}
2022-10-03 11:53:27 +03:00
func NewLeaderboardHandler(userService services.IUserService, leaderboardService services.ILeaderboardService) *LeaderboardHandler {
return &LeaderboardHandler{
config: conf.Get(),
userService: userService,
leaderboardService: leaderboardService,
}
}
func (h *LeaderboardHandler) RegisterRoutes(router *mux.Router) {
r := router.PathPrefix("/leaderboard").Subrouter()
r.Use(
middlewares.NewAuthenticateMiddleware(h.userService).
WithRedirectTarget(defaultErrorRedirectTarget()).
WithOptionalFor([]string{"/"}).
Handler,
)
r.Methods(http.MethodGet).HandlerFunc(h.GetIndex)
}
func (h *LeaderboardHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
if h.config.IsDev() {
loadTemplates()
}
if err := templates[conf.LeaderboardTemplate].Execute(w, h.buildViewModel(r)); err != nil {
logbuch.Error(err.Error())
}
2022-10-03 11:53:27 +03:00
}
func (h *LeaderboardHandler) buildViewModel(r *http.Request) *view.LeaderboardViewModel {
user := middlewares.GetPrincipal(r)
byParam := strings.ToLower(r.URL.Query().Get("by"))
keyParam := strings.ToLower(r.URL.Query().Get("key"))
pageParams := utils.ParsePageParamsWithDefault(r, 1, 100)
// note: pagination is not fully implemented, yet
// count function to get total item / total pages is missing
// and according ui (+ optionally search bar) is missing, too
2022-10-03 11:53:27 +03:00
var err error
var leaderboard models.Leaderboard
2022-10-05 22:52:10 +03:00
var userLanguages map[string][]string
var topKeys []string
2022-10-03 11:53:27 +03:00
if byParam == "" {
leaderboard, err = h.leaderboardService.GetByInterval(models.IntervalPast7Days, pageParams, true)
if err != nil {
conf.Log().Request(r).Error("error while fetching general leaderboard items - %v", err)
return &view.LeaderboardViewModel{Error: criticalError}
}
// regardless of page, always show own rank
if user != nil && !leaderboard.HasUser(user.ID) {
// but only if leaderboard spans multiple pages
if count, err := h.leaderboardService.CountUsers(); err == nil && count > int64(pageParams.PageSize) {
if l, err := h.leaderboardService.GetByIntervalAndUser(models.IntervalPast7Days, user.ID, true); err == nil && len(l) > 0 {
leaderboard = append(leaderboard, l[0])
}
}
}
} else {
if by, ok := allowedAggregations[byParam]; ok {
leaderboard, err = h.leaderboardService.GetAggregatedByInterval(models.IntervalPast7Days, &by, pageParams, true)
if err != nil {
conf.Log().Request(r).Error("error while fetching general leaderboard items - %v", err)
return &view.LeaderboardViewModel{Error: criticalError}
}
// regardless of page, always show own rank
if user != nil {
// but only if leaderboard could, in theory, span multiple pages
if count, err := h.leaderboardService.CountUsers(); err == nil && count > int64(pageParams.PageSize) {
if l, err := h.leaderboardService.GetAggregatedByIntervalAndUser(models.IntervalPast7Days, user.ID, &by, true); err == nil {
leaderboard.AddMany(l)
} else {
conf.Log().Request(r).Error("error while fetching own aggregated user leaderboard - %v", err)
}
}
}
userLeaderboards := slice.GroupWith[*models.LeaderboardItemRanked, string](leaderboard, func(item *models.LeaderboardItemRanked) string {
2022-10-05 22:52:10 +03:00
return item.UserID
})
userLanguages = map[string][]string{}
for u, items := range userLeaderboards {
userLanguages[u] = models.Leaderboard(items).TopKeysByUser(models.SummaryLanguage, u)
}
topKeys = leaderboard.TopKeys(by)
if len(topKeys) > 0 {
if keyParam == "" {
keyParam = topKeys[0]
}
keyParam = strings.ToLower(keyParam)
leaderboard = leaderboard.TopByKey(by, keyParam)
}
} else {
return &view.LeaderboardViewModel{Error: fmt.Sprintf("unsupported aggregation '%s'", byParam)}
}
2022-10-03 11:53:27 +03:00
}
var apiKey string
if user != nil {
apiKey = user.ApiKey
}
return &view.LeaderboardViewModel{
2022-10-05 22:52:10 +03:00
User: user,
By: byParam,
Key: keyParam,
Items: leaderboard,
UserLanguages: userLanguages,
TopKeys: topKeys,
ApiKey: apiKey,
PageParams: pageParams,
2022-10-05 22:52:10 +03:00
Success: r.URL.Query().Get("success"),
Error: r.URL.Query().Get("error"),
2022-10-03 11:53:27 +03:00
}
}