lots of stuff, don't truly remember

This commit is contained in:
Blake Ridgway
2026-05-17 20:39:47 -05:00
parent 178ffb3425
commit dc4fe558b7
35 changed files with 3501 additions and 112 deletions

View File

@@ -43,6 +43,21 @@ type PeriodStats struct {
AvgHR float64 `json:"avg_hr"`
}
// DailyTSS holds the computed TSS for a single day.
type DailyTSS struct {
Date string `json:"date"`
TSS float64 `json:"tss"`
}
// PowerPoint holds power data for a single completed workout.
type PowerPoint struct {
Date string `json:"date"`
AvgPower int `json:"avg_power"`
MaxPower int `json:"max_power"`
Duration int `json:"duration"`
Title string `json:"title"`
}
// GetSummary returns overall ride statistics for completed workouts.
func (r *Repository) GetSummary(userID uint) (*Summary, error) {
var summary Summary
@@ -120,6 +135,65 @@ func (r *Repository) GetMonthlyStats(userID uint, months int) ([]PeriodStats, er
return stats, err
}
// GetDailyTSS returns daily TSS values for the last N days.
// TSS = (duration_seconds * (avg_power / FTP)^2 / 3600) * 100
// Simplified: (duration * avg_power^2) / (FTP^2 * 36)
func (r *Repository) GetDailyTSS(userID uint, ftp int, days int) ([]DailyTSS, error) {
if ftp <= 0 {
return []DailyTSS{}, nil
}
cutoff := time.Now().AddDate(0, 0, -days)
ftpFloat := float64(ftp)
var results []DailyTSS
err := database.DB.Model(&workout.Workout{}).
Select(`
TO_CHAR(scheduled_date, 'YYYY-MM-DD') as date,
COALESCE(SUM(
CASE WHEN avg_power > 0 AND duration > 0
THEN (duration::float8 * avg_power::float8 * avg_power::float8) / (? * ? * 36.0)
ELSE 0
END
), 0) as tss
`, ftpFloat, ftpFloat).
Where("user_id = ? AND status = ? AND scheduled_date >= ?", userID, "completed", cutoff).
Group("date").
Order("date ASC").
Scan(&results).Error
if results == nil {
results = []DailyTSS{}
}
return results, err
}
// GetPowerHistory returns power data points for completed workouts in the last N days.
func (r *Repository) GetPowerHistory(userID uint, days int) ([]PowerPoint, error) {
cutoff := time.Now().AddDate(0, 0, -days)
var results []PowerPoint
err := database.DB.Model(&workout.Workout{}).
Select(`
TO_CHAR(scheduled_date, 'YYYY-MM-DD') as date,
avg_power,
max_power,
duration,
title
`).
Where("user_id = ? AND status = ? AND avg_power > 0 AND scheduled_date >= ?",
userID, "completed", cutoff).
Order("scheduled_date ASC").
Scan(&results).Error
if results == nil {
results = []PowerPoint{}
}
return results, err
}
// PersonalBest holds a single personal best record.
type PersonalBest struct {
Category string `json:"category"`