Skip to content

Commit

Permalink
Show split statistics for different distance-based workouts
Browse files Browse the repository at this point in the history
Signed-off-by: Jo Vandeginste <[email protected]>
  • Loading branch information
jovandeginste committed Feb 25, 2024
1 parent 384887b commit 3ae8e39
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 110 deletions.
210 changes: 115 additions & 95 deletions pkg/database/user_statistics.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@ type (
ID uint
}

WorkoutStatistics map[string]*WorkoutStatistic

WorkoutStatistic struct {
WorkoutType WorkoutType
Total Totals
PerYear map[int]*Totals
PerMonth map[int]map[int]*Totals
Records WorkoutRecord
}

Totals struct {
WorkoutType WorkoutType
Workouts int
Distance float64
Up float64
Expand All @@ -22,26 +33,33 @@ type (
AverageSpeedNoPause float64
MaxSpeed float64
}
UserStatistics struct {
Total Totals
PerYear map[int]*Totals
PerMonth map[int]map[int]*Totals
Records struct {
Active bool
AverageSpeed record
AverageSpeedNoPause record
MaxSpeed record
Distance record
TotalUp record
Duration struct {
Value time.Duration
Date time.Time
ID uint
}

WorkoutRecord struct {
WorkoutType WorkoutType
Active bool
AverageSpeed record
AverageSpeedNoPause record
MaxSpeed record
Distance record
TotalUp record
Duration struct {
Value time.Duration
Date time.Time
ID uint
}
}
)

func NewWorkoutStatistic(t WorkoutType) *WorkoutStatistic {
return &WorkoutStatistic{
WorkoutType: t,
Records: WorkoutRecord{WorkoutType: t},
Total: Totals{WorkoutType: t},
PerYear: map[int]*Totals{},
PerMonth: map[int]map[int]*Totals{},
}
}

func (r *record) CheckAndSwap(value float64, id uint, date *time.Time) {
if r.Value < value {
r.Value = value
Expand All @@ -50,7 +68,7 @@ func (r *record) CheckAndSwap(value float64, id uint, date *time.Time) {
}
}

func (us *UserStatistics) Add(w *Workout) {
func (us *WorkoutStatistic) Add(w *Workout) {
us.Total.Workouts++
us.Total.Distance += w.Data.TotalDistance
us.Total.Duration += w.Data.TotalDuration
Expand All @@ -63,69 +81,61 @@ func (us *UserStatistics) Add(w *Workout) {
year := d.Year()
month := int(d.Month())

us.AddYear(year, w)
us.AddMonth(year, month, w)
us.AddYear(us.WorkoutType, year, w)
us.AddMonth(us.WorkoutType, year, month, w)
}

func (us *UserStatistics) AddMonth(year, month int, w *Workout) {
func NewTotal(t WorkoutType, d *MapData) *Totals {
return &Totals{
Workouts: 1,
WorkoutType: t,
Distance: d.TotalDistance,
Up: d.TotalUp,
Duration: d.TotalDuration,
AverageSpeed: d.AverageSpeed(),
AverageSpeedNoPause: d.AverageSpeedNoPause(),
MaxSpeed: d.MaxSpeed,
}
}

func (t *Totals) Add(d *MapData) {
t.Workouts++
t.Distance += d.TotalDistance
t.Duration += d.TotalDuration
t.Up += d.TotalUp
t.AverageSpeed += d.AverageSpeed()
t.AverageSpeedNoPause += d.AverageSpeedNoPause()
t.MaxSpeed += d.MaxSpeed
}

func (us *WorkoutStatistic) AddMonth(t WorkoutType, year, month int, w *Workout) {
if _, ok := us.PerMonth[year]; !ok {
us.PerMonth[year] = map[int]*Totals{}
}

entry, ok := us.PerMonth[year][month]
if !ok {
us.PerMonth[year][month] = &Totals{
Workouts: 1,
Distance: w.Data.TotalDistance,
Up: w.Data.TotalUp,
Duration: w.Data.TotalDuration,
AverageSpeed: w.Data.AverageSpeed(),
AverageSpeedNoPause: w.Data.AverageSpeedNoPause(),
MaxSpeed: w.Data.MaxSpeed,
}
us.PerMonth[year][month] = NewTotal(t, w.Data)

return
}

entry.Workouts++
entry.Distance += w.Data.TotalDistance
entry.Duration += w.Data.TotalDuration
entry.Up += w.Data.TotalUp
entry.AverageSpeed += w.Data.AverageSpeed()
entry.AverageSpeedNoPause += w.Data.AverageSpeedNoPause()
entry.MaxSpeed += w.Data.MaxSpeed
entry.Add(w.Data)
}

func (us *UserStatistics) AddYear(year int, w *Workout) {
func (us *WorkoutStatistic) AddYear(t WorkoutType, year int, w *Workout) {
entry, ok := us.PerYear[year]
if !ok {
us.PerYear[year] = &Totals{
Workouts: 1,
Distance: w.Data.TotalDistance,
Up: w.Data.TotalUp,
Duration: w.Data.TotalDuration,
AverageSpeed: w.Data.AverageSpeed(),
AverageSpeedNoPause: w.Data.AverageSpeedNoPause(),
MaxSpeed: w.Data.MaxSpeed,
}
us.PerYear[year] = NewTotal(t, w.Data)

return
}

entry.Workouts++
entry.Distance += w.Data.TotalDistance
entry.Duration += w.Data.TotalDuration
entry.Up += w.Data.TotalUp
entry.AverageSpeed += w.Data.AverageSpeed()
entry.AverageSpeedNoPause += w.Data.AverageSpeedNoPause()
entry.MaxSpeed += w.Data.MaxSpeed
entry.Add(w.Data)
}

func (u *User) Statistics(db *gorm.DB) (*UserStatistics, error) {
us := &UserStatistics{}

us.PerYear = map[int]*Totals{}
us.PerMonth = map[int]map[int]*Totals{}
func (u *User) Statistics(db *gorm.DB) (WorkoutStatistics, error) {
us := WorkoutStatistics{}

workouts, err := u.GetWorkouts(db)
if err != nil {
Expand All @@ -137,45 +147,55 @@ func (u *User) Statistics(db *gorm.DB) (*UserStatistics, error) {
continue
}

us.Records.Active = true
us.Add(w)

us.Records.AverageSpeedNoPause.CheckAndSwap(
w.Data.AverageSpeedNoPause(),
w.ID,
w.Date,
)

us.Records.AverageSpeed.CheckAndSwap(
w.Data.AverageSpeed(),
w.ID,
w.Date,
)

us.Records.MaxSpeed.CheckAndSwap(
w.Data.MaxSpeed,
w.ID,
w.Date,
)

us.Records.Distance.CheckAndSwap(
w.Data.TotalDistance,
w.ID,
w.Date,
)

us.Records.TotalUp.CheckAndSwap(
w.Data.TotalUp,
w.ID,
w.Date,
)

if w.Data.TotalDuration > us.Records.Duration.Value {
us.Records.Duration.Value = w.Data.TotalDuration
us.Records.Duration.ID = w.ID
us.Records.Duration.Date = *w.Date
s, ok := us[w.Type.String()]
if !ok {
s = NewWorkoutStatistic(w.Type)
us[w.Type.String()] = s
}

s.Records.Active = true
s.Add(w)

s.Records.CheckAndSwap(w)
}

return us, nil
}

func (wr *WorkoutRecord) CheckAndSwap(w *Workout) {
wr.AverageSpeedNoPause.CheckAndSwap(
w.Data.AverageSpeedNoPause(),
w.ID,
w.Date,
)

wr.AverageSpeed.CheckAndSwap(
w.Data.AverageSpeed(),
w.ID,
w.Date,
)

wr.MaxSpeed.CheckAndSwap(
w.Data.MaxSpeed,
w.ID,
w.Date,
)

wr.Distance.CheckAndSwap(
w.Data.TotalDistance,
w.ID,
w.Date,
)

wr.TotalUp.CheckAndSwap(
w.Data.TotalUp,
w.ID,
w.Date,
)

if w.Data.TotalDuration > wr.Duration.Value {
wr.Duration.Value = w.Data.TotalDuration
wr.Duration.ID = w.ID
wr.Duration.Date = *w.Date
}
}
2 changes: 2 additions & 0 deletions translations/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,13 @@
"Your account has been created, but needs to be activated.": "Your account has been created, but needs to be activated.",
"Your profile": "Your profile",
"Your progress the past year": "Your progress the past year",
"cycling": "cycling",
"delete": "delete",
"distance": "distance",
"duration": "duration",
"edit": "edit",
"refresh": "refresh",
"running": "running",
"up": "up",
"user": "user",
"workout": "workout",
Expand Down
2 changes: 2 additions & 0 deletions translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,13 @@
"Your account has been created, but needs to be activated.": "Jouw account is aangemaakt, maar moet nog worden geactiveerd.",
"Your profile": "Jouw profiel",
"Your progress the past year": "Jouw vooruitgang afgelopen jaar",
"cycling": "fietsen",
"delete": "verwijder",
"distance": "afstand",
"duration": "duur",
"edit": "bewerk",
"refresh": "ververs",
"running": "lopen",
"up": "omhoog",
"user": "gebruiker",
"workout": "workout",
Expand Down
2 changes: 2 additions & 0 deletions views/partials/messages.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
{{ i18n "The user '%s' has been updated." .Name }}
{{ i18n "The user '%s' has been deleted." .Name }}
{{ i18n "Added %d new workout(s): %s" (len .msg) .msg }}
{{ i18n "running" }}
{{ i18n "cycling" }}
</pre>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{ define "starts_record_running_date" }}
{{ define "stats_record_distance_date" }}
<span class="hidden 2xl:inline" title="{{ .Date | LocalDate }}">
<a href="{{ RouteFor `workout-show` .ID }}">{{ .Date | LocalDate }}</a>
({{ .Date | RelativeDate }})
Expand All @@ -13,10 +13,10 @@
>{{ .Date.Format "2006-01-02" }}</a
>
</span>
{{ end }} {{ define "stats_records_running" }} {{ if .Active }}
{{ end }} {{ define "stats_records_distance" }} {{ if .Active }}
<h3>
<span class="{{ IconFor `running` }}"></span>
{{ i18n "Running records" }}
<span class="{{ IconFor .WorkoutType.String }}"></span>
{{ i18n "%s records" .WorkoutType.String }}
</h3>
<table class="workout-info table-auto">
<tbody>
Expand All @@ -26,7 +26,7 @@ <h3>
<span class="{{ IconFor `speed` }}">{{ i18n "Average speed" }}</span>
</th>
<td class="font-mono whitespace-nowrap">{{ .Value | HumanSpeed }}</td>
<td>{{ template "starts_record_running_date" . }}</td>
<td>{{ template "stats_record_distance_date" . }}</td>
</tr>
{{ end }} {{ with .AverageSpeedNoPause }}
<tr>
Expand All @@ -36,15 +36,15 @@ <h3>
>
</th>
<td class="font-mono whitespace-nowrap">{{ .Value | HumanSpeed }}</td>
<td>{{ template "starts_record_running_date" . }}</td>
<td>{{ template "stats_record_distance_date" . }}</td>
</tr>
{{ end }} {{ with .MaxSpeed }}
<tr>
<th>
<span class="{{ IconFor `max-speed` }}">{{ i18n "Max speed" }}</span>
</th>
<td class="font-mono whitespace-nowrap">{{ .Value | HumanSpeed }}</td>
<td>{{ template "starts_record_running_date" . }}</td>
<td>{{ template "stats_record_distance_date" . }}</td>
</tr>
{{ end }} {{ with .Distance }}
<tr>
Expand All @@ -54,15 +54,15 @@ <h3>
>
</th>
<td class="font-mono whitespace-nowrap">{{ .Value | HumanDistance }}</td>
<td>{{ template "starts_record_running_date" . }}</td>
<td>{{ template "stats_record_distance_date" . }}</td>
</tr>
{{ end }} {{ with .TotalUp }}
<tr>
<th>
<span class="{{ IconFor `up` }}">{{ i18n "Total up" }}</span>
</th>
<td class="font-mono whitespace-nowrap">{{ .Value | HumanDistance }}</td>
<td>{{ template "starts_record_running_date" . }}</td>
<td>{{ template "stats_record_distance_date" . }}</td>
</tr>
{{ end }} {{ with .Duration }}
<tr>
Expand All @@ -72,7 +72,7 @@ <h3>
>
</th>
<td class="font-mono whitespace-nowrap">{{ .Value | HumanDuration }}</td>
<td>{{ template "starts_record_running_date" . }}</td>
<td>{{ template "stats_record_distance_date" . }}</td>
</tr>
{{ end }}
</tbody>
Expand Down
4 changes: 3 additions & 1 deletion views/partials/workout_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
<tr>
<td class="{{ IconFor `workout` }}"></td>
<th>{{ i18n "Type" }}</th>
<td>{{ .Type.String }}</td>
<td>
<span class="{{ IconFor .Type.String }}">{{ i18n .Type.String }}</span>
</td>
</tr>
<tr>
<td class="{{ IconFor `duration` }}"></td>
Expand Down
Loading

0 comments on commit 3ae8e39

Please sign in to comment.