mirror of
https://github.com/muety/wakapi.git
synced 2025-12-05 22:20:24 -08:00
feat: add user preference for start of week
This commit is contained in:
@@ -2,9 +2,10 @@ package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ParseInterval(interval string) (*models.IntervalKey, error) {
|
||||
@@ -21,20 +22,20 @@ func MustParseInterval(interval string) *models.IntervalKey {
|
||||
return key
|
||||
}
|
||||
|
||||
func MustResolveIntervalRawTZ(interval string, tz *time.Location) (from, to time.Time) {
|
||||
_, from, to = ResolveIntervalRawTZ(interval, tz)
|
||||
func MustResolveIntervalRawTZ(interval string, tz *time.Location, startOfWeek time.Weekday) (from, to time.Time) {
|
||||
_, from, to = ResolveIntervalRawTZ(interval, tz, startOfWeek)
|
||||
return from, to
|
||||
}
|
||||
|
||||
func ResolveIntervalRawTZ(interval string, tz *time.Location) (err error, from, to time.Time) {
|
||||
func ResolveIntervalRawTZ(interval string, tz *time.Location, startOfWeek time.Weekday) (err error, from, to time.Time) {
|
||||
parsed, err := ParseInterval(interval)
|
||||
if err != nil {
|
||||
return err, time.Time{}, time.Time{}
|
||||
}
|
||||
return ResolveIntervalTZ(parsed, tz)
|
||||
return ResolveIntervalTZ(parsed, tz, startOfWeek)
|
||||
}
|
||||
|
||||
func ResolveIntervalTZ(interval *models.IntervalKey, tz *time.Location) (err error, from, to time.Time) {
|
||||
func ResolveIntervalTZ(interval *models.IntervalKey, tz *time.Location, startOfWeek time.Weekday) (err error, from, to time.Time) {
|
||||
now := time.Now().In(tz)
|
||||
to = now
|
||||
|
||||
@@ -47,10 +48,10 @@ func ResolveIntervalTZ(interval *models.IntervalKey, tz *time.Location) (err err
|
||||
case models.IntervalPastDay:
|
||||
from = now.Add(-24 * time.Hour)
|
||||
case models.IntervalThisWeek:
|
||||
from = utils.BeginOfThisWeek(tz)
|
||||
from = utils.BeginOfThisWeek(tz, startOfWeek)
|
||||
case models.IntervalLastWeek:
|
||||
from = utils.BeginOfThisWeek(tz).AddDate(0, 0, -7)
|
||||
to = utils.BeginOfThisWeek(tz)
|
||||
from = utils.BeginOfThisWeek(tz, startOfWeek).AddDate(0, 0, -7)
|
||||
to = utils.BeginOfThisWeek(tz, startOfWeek)
|
||||
case models.IntervalThisMonth:
|
||||
from = utils.BeginOfThisMonth(tz)
|
||||
case models.IntervalLastMonth:
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestResolveMaximumRange_Default(t *testing.T) {
|
||||
for i := 1; i <= 367; i++ {
|
||||
err1, maximumInterval := ResolveMaximumRange(i)
|
||||
err2, from, to := ResolveIntervalTZ(maximumInterval, time.UTC)
|
||||
err2, from, to := ResolveIntervalTZ(maximumInterval, time.UTC, time.Monday)
|
||||
|
||||
assert.Nil(t, err1)
|
||||
assert.Nil(t, err2)
|
||||
|
||||
@@ -2,9 +2,10 @@ package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/muety/wakapi/models"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/muety/wakapi/models"
|
||||
)
|
||||
|
||||
func ParseSummaryParams(r *http.Request) (*models.SummaryParams, error) {
|
||||
@@ -15,9 +16,9 @@ func ParseSummaryParams(r *http.Request) (*models.SummaryParams, error) {
|
||||
var from, to time.Time
|
||||
|
||||
if interval := params.Get("interval"); interval != "" {
|
||||
err, from, to = ResolveIntervalRawTZ(interval, user.TZ())
|
||||
err, from, to = ResolveIntervalRawTZ(interval, user.TZ(), user.StartOfWeekDay())
|
||||
} else if start := params.Get("start"); start != "" {
|
||||
err, from, to = ResolveIntervalRawTZ(start, user.TZ())
|
||||
err, from, to = ResolveIntervalRawTZ(start, user.TZ(), user.StartOfWeekDay())
|
||||
} else {
|
||||
from, err = ParseDateTimeTZ(params.Get("from"), user.TZ())
|
||||
if err != nil {
|
||||
|
||||
41
migrations/20250818_add_start_of_week.go
Normal file
41
migrations/20250818_add_start_of_week.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/muety/wakapi/config"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
const name = "20250818-add_start-of-week"
|
||||
f := migrationFunc{
|
||||
name: name,
|
||||
background: true,
|
||||
f: func(db *gorm.DB, cfg *config.Config) error {
|
||||
if hasRun(name, db) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if column already exists
|
||||
var count int64
|
||||
if err := db.Raw("SELECT COUNT(*) FROM pragma_table_info('users') WHERE name = 'start_of_week'").Scan(&count).Error; err != nil {
|
||||
// If pragma_table_info fails (e.g., MySQL/PostgreSQL), try a different approach
|
||||
if err := db.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'start_of_week'").Scan(&count).Error; err != nil {
|
||||
// If that also fails, assume column doesn't exist and proceed
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Only add column if it doesn't exist
|
||||
if count == 0 {
|
||||
if err := db.Exec("ALTER TABLE users ADD COLUMN start_of_week INTEGER DEFAULT 1").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
setHasRun(name, db)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
registerPostMigration(f)
|
||||
}
|
||||
@@ -28,6 +28,7 @@ type User struct {
|
||||
ApiKey string `json:"api_key" gorm:"unique; default:NULL"`
|
||||
Email string `json:"email" gorm:"index:idx_user_email; size:255"`
|
||||
Location string `json:"location"`
|
||||
StartOfWeek int `json:"start_of_week" gorm:"default:1"`
|
||||
Password string `json:"-"`
|
||||
CreatedAt CustomTime `swaggertype:"string" format:"date" example:"2006-01-02 15:04:05.000"` // filled by gorm, see https://gorm.io/docs/conventions.html#CreatedAt
|
||||
LastLoggedInAt CustomTime `swaggertype:"string" format:"date" example:"2006-01-02 15:04:05.000"` // filled by gorm, see https://gorm.io/docs/conventions.html#CreatedAt
|
||||
@@ -90,6 +91,7 @@ type CredentialsReset struct {
|
||||
type UserDataUpdate struct {
|
||||
Email string `schema:"email"`
|
||||
Location string `schema:"location"`
|
||||
StartOfWeek int `schema:"start_of_week"`
|
||||
ReportsWeekly bool `schema:"reports_weekly"`
|
||||
PublicLeaderboard bool `schema:"public_leaderboard"`
|
||||
}
|
||||
@@ -126,6 +128,14 @@ func (u *User) TZOffset() time.Duration {
|
||||
return time.Duration(offset * int(time.Second))
|
||||
}
|
||||
|
||||
// StartOfWeekDay returns the user's preferred start of week as time.Weekday
|
||||
func (u *User) StartOfWeekDay() time.Weekday {
|
||||
if u.StartOfWeek < 0 || u.StartOfWeek > 6 {
|
||||
u.StartOfWeek = 1 // Default to Monday
|
||||
}
|
||||
return time.Weekday(u.StartOfWeek)
|
||||
}
|
||||
|
||||
func (u *User) AvatarURL(urlTemplate string) string {
|
||||
urlTemplate = strings.ReplaceAll(urlTemplate, "{username}", u.ID)
|
||||
urlTemplate = strings.ReplaceAll(urlTemplate, "{email}", u.Email)
|
||||
@@ -215,7 +225,7 @@ func (s *Signup) IsValid() bool {
|
||||
}
|
||||
|
||||
func (r *UserDataUpdate) IsValid() bool {
|
||||
return ValidateEmail(r.Email) && ValidateTimezone(r.Location)
|
||||
return ValidateEmail(r.Email) && ValidateTimezone(r.Location) && ValidateStartOfWeek(r.StartOfWeek)
|
||||
}
|
||||
|
||||
func ValidateUsername(username string) bool {
|
||||
@@ -241,3 +251,7 @@ func ValidateTimezone(tz string) bool {
|
||||
_, err := time.LoadLocation(tz)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func ValidateStartOfWeek(startOfWeek int) bool {
|
||||
return startOfWeek >= 0 && startOfWeek <= 6
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ package repositories
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"gorm.io/gorm"
|
||||
@@ -144,6 +145,7 @@ func (r *UserRepository) Update(user *models.User) (*models.User, error) {
|
||||
"has_data": user.HasData,
|
||||
"reset_token": user.ResetToken,
|
||||
"location": user.Location,
|
||||
"start_of_week": user.StartOfWeek,
|
||||
"reports_weekly": user.ReportsWeekly,
|
||||
"public_leaderboard": user.PublicLeaderboard,
|
||||
"subscribed_until": user.SubscribedUntil,
|
||||
|
||||
@@ -2,6 +2,13 @@ package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/alitto/pond/v2"
|
||||
"github.com/go-chi/chi/v5"
|
||||
conf "github.com/muety/wakapi/config"
|
||||
@@ -13,12 +20,6 @@ import (
|
||||
"github.com/muety/wakapi/repositories"
|
||||
"github.com/muety/wakapi/services"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -145,7 +146,7 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
from, to := helpers.MustResolveIntervalRawTZ("today", user.TZ())
|
||||
from, to := helpers.MustResolveIntervalRawTZ("today", user.TZ(), user.StartOfWeekDay())
|
||||
|
||||
summaryToday, err := h.summarySrvc.Aliased(from, to, user, h.summarySrvc.Retrieve, nil, nil, false)
|
||||
if err != nil {
|
||||
@@ -455,7 +456,7 @@ func (h *MetricsHandler) getAdminMetrics(user *models.User) (*mm.Metrics, error)
|
||||
|
||||
// Get per-user total activity
|
||||
|
||||
_, from, to := helpers.ResolveIntervalTZ(models.IntervalAny, time.Local)
|
||||
_, from, to := helpers.ResolveIntervalTZ(models.IntervalAny, time.Local, time.Monday)
|
||||
to = to.Truncate(time.Hour)
|
||||
|
||||
wp := pond.NewPool(utils.HalfCPUs())
|
||||
|
||||
@@ -2,12 +2,13 @@ package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/muety/wakapi/helpers"
|
||||
"github.com/muety/wakapi/models/types"
|
||||
routeutils "github.com/muety/wakapi/routes/utils"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
conf "github.com/muety/wakapi/config"
|
||||
"github.com/muety/wakapi/models"
|
||||
@@ -88,7 +89,7 @@ func (h *BadgeHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (h *BadgeHandler) loadUserSummary(user *models.User, interval *models.IntervalKey, filters *models.Filters) (*models.Summary, error, int) {
|
||||
err, from, to := helpers.ResolveIntervalTZ(interval, user.TZ())
|
||||
err, from, to := helpers.ResolveIntervalTZ(interval, user.TZ(), user.StartOfWeekDay())
|
||||
if err != nil {
|
||||
return nil, err, http.StatusBadRequest
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"math"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/muety/wakapi/helpers"
|
||||
"github.com/muety/wakapi/middlewares"
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"math"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
conf "github.com/muety/wakapi/config"
|
||||
v1 "github.com/muety/wakapi/models/compat/wakatime/v1"
|
||||
@@ -120,7 +121,7 @@ func (h *LeadersHandler) buildViewModel(globalLeaderboard, languageLeaderboard m
|
||||
totalUsers, _ := h.leaderboardSrvc.CountUsers(true)
|
||||
totalPages := int(totalUsers/int64(pageParams.PageSize) + 1)
|
||||
|
||||
_, from, to := helpers.ResolveIntervalTZ(interval, time.UTC)
|
||||
_, from, to := helpers.ResolveIntervalTZ(interval, time.UTC, time.Monday)
|
||||
numDays := len(utils.SplitRangeByDays(from, to))
|
||||
|
||||
vm := &v1.LeadersViewModel{
|
||||
|
||||
@@ -87,7 +87,7 @@ func (h *StatsHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
err, rangeFrom, rangeTo := helpers.ResolveIntervalRawTZ(rangeParam, requestedUser.TZ())
|
||||
err, rangeFrom, rangeTo := helpers.ResolveIntervalRawTZ(rangeParam, requestedUser.TZ(), requestedUser.StartOfWeekDay())
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("invalid range"))
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/muety/wakapi/helpers"
|
||||
"github.com/muety/wakapi/models/types"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
conf "github.com/muety/wakapi/config"
|
||||
"github.com/muety/wakapi/middlewares"
|
||||
@@ -63,7 +64,7 @@ func (h *StatusBarHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
rangeParam = (*models.IntervalToday)[0]
|
||||
}
|
||||
|
||||
err, rangeFrom, rangeTo := helpers.ResolveIntervalRawTZ(rangeParam, user.TZ())
|
||||
err, rangeFrom, rangeTo := helpers.ResolveIntervalRawTZ(rangeParam, user.TZ(), user.StartOfWeekDay())
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("invalid range"))
|
||||
|
||||
@@ -2,13 +2,14 @@ package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/muety/wakapi/helpers"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/muety/wakapi/helpers"
|
||||
|
||||
conf "github.com/muety/wakapi/config"
|
||||
"github.com/muety/wakapi/middlewares"
|
||||
"github.com/muety/wakapi/models"
|
||||
@@ -84,6 +85,7 @@ func (h *SummariesHandler) loadUserSummaries(r *http.Request, user *models.User)
|
||||
rangeParam, startParam, endParam, tzParam := params.Get("range"), params.Get("start"), params.Get("end"), params.Get("timezone")
|
||||
|
||||
timezone := user.TZ()
|
||||
startOfWeek := user.StartOfWeekDay()
|
||||
if tzParam != "" {
|
||||
if tz, err := time.LoadLocation(tzParam); err == nil {
|
||||
timezone = tz
|
||||
@@ -93,12 +95,12 @@ func (h *SummariesHandler) loadUserSummaries(r *http.Request, user *models.User)
|
||||
var start, end time.Time
|
||||
if rangeParam != "" {
|
||||
// range param takes precedence
|
||||
if err, parsedFrom, parsedTo := helpers.ResolveIntervalRawTZ(rangeParam, timezone); err == nil {
|
||||
if err, parsedFrom, parsedTo := helpers.ResolveIntervalRawTZ(rangeParam, timezone, startOfWeek); err == nil {
|
||||
start, end = parsedFrom, parsedTo
|
||||
} else {
|
||||
return nil, errors.New("invalid 'range' parameter"), http.StatusBadRequest
|
||||
}
|
||||
} else if err, parsedFrom, parsedTo := helpers.ResolveIntervalRawTZ(startParam, timezone); err == nil && startParam == endParam {
|
||||
} else if err, parsedFrom, parsedTo := helpers.ResolveIntervalRawTZ(startParam, timezone, startOfWeek); err == nil && startParam == endParam {
|
||||
// also accept start param to be a range param
|
||||
start, end = parsedFrom, parsedTo
|
||||
} else {
|
||||
|
||||
@@ -3,10 +3,6 @@ package routes
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/muety/wakapi/helpers"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
@@ -14,6 +10,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/muety/wakapi/helpers"
|
||||
|
||||
"log/slog"
|
||||
|
||||
datastructure "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
"github.com/gorilla/schema"
|
||||
conf "github.com/muety/wakapi/config"
|
||||
@@ -24,7 +27,6 @@ import (
|
||||
"github.com/muety/wakapi/services"
|
||||
"github.com/muety/wakapi/services/imports"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
const criticalError = "a critical error has occurred, sorry"
|
||||
@@ -220,6 +222,7 @@ func (h *SettingsHandler) actionUpdateUser(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
user.Email = payload.Email
|
||||
user.Location = payload.Location
|
||||
user.StartOfWeek = payload.StartOfWeek
|
||||
user.ReportsWeekly = payload.ReportsWeekly
|
||||
user.PublicLeaderboard = payload.PublicLeaderboard
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/muety/wakapi/helpers"
|
||||
"github.com/muety/wakapi/models"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -37,7 +38,7 @@ func GetBadgeParams(reqPath string, authorizedUser, requestedUser *models.User)
|
||||
}
|
||||
}
|
||||
|
||||
_, rangeFrom, rangeTo := helpers.ResolveIntervalTZ(intervalKey, requestedUser.TZ())
|
||||
_, rangeFrom, rangeTo := helpers.ResolveIntervalTZ(intervalKey, requestedUser.TZ(), requestedUser.StartOfWeekDay())
|
||||
interval := &models.KeyedInterval{
|
||||
Interval: models.Interval{Start: rangeFrom, End: rangeTo},
|
||||
Key: intervalKey,
|
||||
|
||||
@@ -5,6 +5,10 @@ import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
svg "github.com/ajstarks/svgo/float"
|
||||
"github.com/alitto/pond/v2"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
@@ -14,9 +18,6 @@ import (
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -67,7 +68,8 @@ func (s *ActivityService) GetChart(user *models.User, interval *models.IntervalK
|
||||
}
|
||||
|
||||
func (s *ActivityService) getChartPastYear(user *models.User, darkTheme, hideAttribution bool) (string, error) {
|
||||
err, from, to := helpers.ResolveIntervalTZ(models.IntervalPast12Months, user.TZ())
|
||||
err, from, to := helpers.ResolveIntervalTZ(models.IntervalPast12Months, user.TZ(), user.StartOfWeekDay())
|
||||
// TODO: I am not sure if we have to handle startOfWeekDay here, but it seems like we do not need to.
|
||||
from = datetime.BeginOfWeek(from, time.Monday)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -2,6 +2,12 @@ package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leandro-lugaresi/hub"
|
||||
"github.com/muety/artifex/v2"
|
||||
"github.com/muety/wakapi/config"
|
||||
@@ -10,11 +16,6 @@ import (
|
||||
"github.com/muety/wakapi/repositories"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LeaderboardService struct {
|
||||
@@ -225,7 +226,7 @@ func (srv *LeaderboardService) GetAggregatedByIntervalAndUser(interval *models.I
|
||||
}
|
||||
|
||||
func (srv *LeaderboardService) GenerateByUser(user *models.User, interval *models.IntervalKey) (*models.LeaderboardItem, error) {
|
||||
err, from, to := helpers.ResolveIntervalTZ(interval, user.TZ())
|
||||
err, from, to := helpers.ResolveIntervalTZ(interval, user.TZ(), user.StartOfWeekDay())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -247,7 +248,7 @@ func (srv *LeaderboardService) GenerateByUser(user *models.User, interval *model
|
||||
}
|
||||
|
||||
func (srv *LeaderboardService) GenerateAggregatedByUser(user *models.User, interval *models.IntervalKey, by uint8) ([]*models.LeaderboardItem, error) {
|
||||
err, from, to := helpers.ResolveIntervalTZ(interval, user.TZ())
|
||||
err, from, to := helpers.ResolveIntervalTZ(interval, user.TZ(), user.StartOfWeekDay())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
)
|
||||
|
||||
func MustParseTime(layout, value string) time.Time {
|
||||
@@ -15,8 +16,8 @@ func BeginOfToday(tz *time.Location) time.Time {
|
||||
return datetime.BeginOfDay(time.Now().In(tz))
|
||||
}
|
||||
|
||||
func BeginOfThisWeek(tz *time.Location) time.Time {
|
||||
return datetime.BeginOfWeek(time.Now().In(tz), time.Monday)
|
||||
func BeginOfThisWeek(tz *time.Location, startOfWeek time.Weekday) time.Time {
|
||||
return datetime.BeginOfWeek(time.Now().In(tz), startOfWeek)
|
||||
}
|
||||
|
||||
func BeginOfThisMonth(tz *time.Location) time.Time {
|
||||
|
||||
@@ -76,6 +76,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-8">
|
||||
<div class="w-1/2 mr-4 inline-block">
|
||||
<label class="font-semibold text-gray-300" for="select-start-of-week">Start of Week</label>
|
||||
<span class="block text-sm text-gray-600">Choose which day your week starts on. This affects "This Week" and "Last Week" intervals.</span>
|
||||
</div>
|
||||
<div class="w-1/2 ml-4">
|
||||
<select name="start_of_week" id="select-start-of-week" class="select-default">
|
||||
<option value="0" {{ if eq .User.StartOfWeek 0 }}selected{{ end }}>Sunday</option>
|
||||
<option value="1" {{ if eq .User.StartOfWeek 1 }}selected{{ end }}>Monday</option>
|
||||
<option value="2" {{ if eq .User.StartOfWeek 2 }}selected{{ end }}>Tuesday</option>
|
||||
<option value="3" {{ if eq .User.StartOfWeek 3 }}selected{{ end }}>Wednesday</option>
|
||||
<option value="4" {{ if eq .User.StartOfWeek 4 }}selected{{ end }}>Thursday</option>
|
||||
<option value="5" {{ if eq .User.StartOfWeek 5 }}selected{{ end }}>Friday</option>
|
||||
<option value="6" {{ if eq .User.StartOfWeek 6 }}selected{{ end }}>Saturday</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-8">
|
||||
<div class="w-1/2 mr-4 inline-block">
|
||||
<label class="font-semibold text-gray-300" for="email">E-Mail Address</label>
|
||||
|
||||
Reference in New Issue
Block a user