2022-10-03 11:53:27 +03:00
|
|
|
package routes
|
|
|
|
|
|
|
|
import (
|
2022-10-03 21:38:19 +03:00
|
|
|
"fmt"
|
2022-10-05 22:52:10 +03:00
|
|
|
"github.com/duke-git/lancet/v2/slice"
|
2022-10-04 00:52:22 +03:00
|
|
|
"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"
|
2023-01-02 20:05:28 +03:00
|
|
|
routeutils "github.com/muety/wakapi/routes/utils"
|
2022-10-03 11:53:27 +03:00
|
|
|
"github.com/muety/wakapi/services"
|
2022-10-16 19:59:19 +03:00
|
|
|
"github.com/muety/wakapi/utils"
|
2022-10-03 11:53:27 +03:00
|
|
|
"net/http"
|
2022-10-03 21:38:19 +03:00
|
|
|
"strings"
|
2022-10-03 11:53:27 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
type LeaderboardHandler struct {
|
|
|
|
config *conf.Config
|
|
|
|
userService services.IUserService
|
|
|
|
leaderboardService services.ILeaderboardService
|
|
|
|
}
|
|
|
|
|
2022-10-03 21:38:19 +03:00
|
|
|
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()).
|
2023-01-02 20:05:28 +03:00
|
|
|
WithRedirectErrorMessage("unauthorized").
|
2022-10-03 11:53:27 +03:00
|
|
|
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()
|
|
|
|
}
|
2023-01-02 20:05:28 +03:00
|
|
|
if err := templates[conf.LeaderboardTemplate].Execute(w, h.buildViewModel(r, w)); err != nil {
|
2022-10-04 00:52:22 +03:00
|
|
|
logbuch.Error(err.Error())
|
|
|
|
}
|
2022-10-03 11:53:27 +03:00
|
|
|
}
|
|
|
|
|
2023-01-02 20:05:28 +03:00
|
|
|
func (h *LeaderboardHandler) buildViewModel(r *http.Request, w http.ResponseWriter) *view.LeaderboardViewModel {
|
2022-10-03 11:53:27 +03:00
|
|
|
user := middlewares.GetPrincipal(r)
|
2022-10-03 21:38:19 +03:00
|
|
|
byParam := strings.ToLower(r.URL.Query().Get("by"))
|
2022-10-04 00:52:22 +03:00
|
|
|
keyParam := strings.ToLower(r.URL.Query().Get("key"))
|
2022-10-19 19:28:30 +03:00
|
|
|
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
|
|
|
|
2022-10-03 21:38:19 +03:00
|
|
|
var err error
|
2022-10-04 00:52:22 +03:00
|
|
|
var leaderboard models.Leaderboard
|
2022-10-05 22:52:10 +03:00
|
|
|
var userLanguages map[string][]string
|
2022-10-04 00:52:22 +03:00
|
|
|
var topKeys []string
|
2022-10-03 11:53:27 +03:00
|
|
|
|
2022-10-03 21:38:19 +03:00
|
|
|
if byParam == "" {
|
2022-10-16 19:59:19 +03:00
|
|
|
leaderboard, err = h.leaderboardService.GetByInterval(models.IntervalPast7Days, pageParams, true)
|
2022-10-03 21:38:19 +03:00
|
|
|
if err != nil {
|
|
|
|
conf.Log().Request(r).Error("error while fetching general leaderboard items - %v", err)
|
2023-01-02 20:05:28 +03:00
|
|
|
return &view.LeaderboardViewModel{
|
|
|
|
Messages: view.Messages{Error: criticalError},
|
|
|
|
}
|
2022-10-03 21:38:19 +03:00
|
|
|
}
|
2022-10-19 19:28:30 +03:00
|
|
|
|
|
|
|
// 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])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-10-03 21:38:19 +03:00
|
|
|
} else {
|
|
|
|
if by, ok := allowedAggregations[byParam]; ok {
|
2022-10-16 19:59:19 +03:00
|
|
|
leaderboard, err = h.leaderboardService.GetAggregatedByInterval(models.IntervalPast7Days, &by, pageParams, true)
|
2022-10-03 21:38:19 +03:00
|
|
|
if err != nil {
|
|
|
|
conf.Log().Request(r).Error("error while fetching general leaderboard items - %v", err)
|
2023-01-02 20:05:28 +03:00
|
|
|
return &view.LeaderboardViewModel{
|
|
|
|
Messages: view.Messages{Error: criticalError},
|
|
|
|
}
|
2022-10-03 21:38:19 +03:00
|
|
|
}
|
2022-10-04 00:52:22 +03:00
|
|
|
|
2022-10-19 19:28:30 +03:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-20 09:33:12 +03:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2022-10-04 00:52:22 +03:00
|
|
|
topKeys = leaderboard.TopKeys(by)
|
|
|
|
if len(topKeys) > 0 {
|
|
|
|
if keyParam == "" {
|
2022-10-07 09:58:51 +03:00
|
|
|
keyParam = topKeys[0]
|
2022-10-04 00:52:22 +03:00
|
|
|
}
|
2022-10-07 09:58:51 +03:00
|
|
|
keyParam = strings.ToLower(keyParam)
|
2022-10-04 00:52:22 +03:00
|
|
|
leaderboard = leaderboard.TopByKey(by, keyParam)
|
|
|
|
}
|
2022-10-03 21:38:19 +03:00
|
|
|
} else {
|
2023-01-02 20:05:28 +03:00
|
|
|
return &view.LeaderboardViewModel{
|
|
|
|
Messages: view.Messages{Error: fmt.Sprintf("unsupported aggregation '%s'", byParam)},
|
|
|
|
}
|
2022-10-03 21:38:19 +03:00
|
|
|
}
|
2022-10-03 11:53:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var apiKey string
|
|
|
|
if user != nil {
|
|
|
|
apiKey = user.ApiKey
|
|
|
|
}
|
|
|
|
|
2023-01-02 20:05:28 +03:00
|
|
|
vm := &view.LeaderboardViewModel{
|
2022-10-05 22:52:10 +03:00
|
|
|
User: user,
|
|
|
|
By: byParam,
|
|
|
|
Key: keyParam,
|
|
|
|
Items: leaderboard,
|
|
|
|
UserLanguages: userLanguages,
|
|
|
|
TopKeys: topKeys,
|
|
|
|
ApiKey: apiKey,
|
2022-10-16 19:59:19 +03:00
|
|
|
PageParams: pageParams,
|
2022-10-03 11:53:27 +03:00
|
|
|
}
|
2023-01-02 20:05:28 +03:00
|
|
|
return routeutils.WithSessionMessages(vm, r, w)
|
2022-10-03 11:53:27 +03:00
|
|
|
}
|