feat: add per-workout-type pace range and expected HR zone
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
69
backend/internal/store/workoutpaces.go
Normal file
69
backend/internal/store/workoutpaces.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// WorkoutTypePace is a workout kind's user-declared target pace range and
|
||||
// expected HR zone. Informational only -- never read by the classification
|
||||
// rule engine. No history: fields are overwritten in place.
|
||||
type WorkoutTypePace struct {
|
||||
WorkoutKindID int64
|
||||
PaceMinSecPerKm *float64
|
||||
PaceMaxSecPerKm *float64
|
||||
ExpectedHRZone *int
|
||||
}
|
||||
|
||||
func scanWorkoutTypePace(row interface{ Scan(...any) error }) (WorkoutTypePace, error) {
|
||||
var p WorkoutTypePace
|
||||
err := row.Scan(&p.WorkoutKindID, &p.PaceMinSecPerKm, &p.PaceMaxSecPerKm, &p.ExpectedHRZone)
|
||||
return p, err
|
||||
}
|
||||
|
||||
const workoutTypePaceColumns = `workout_kind_id, pace_min_sec_per_km, pace_max_sec_per_km, expected_hr_zone`
|
||||
|
||||
// GetWorkoutTypePace fetches the pace/zone row for one workout kind.
|
||||
func (db *DB) GetWorkoutTypePace(ctx context.Context, workoutKindID int64) (WorkoutTypePace, error) {
|
||||
row := db.QueryRowContext(ctx, `SELECT `+workoutTypePaceColumns+` FROM workout_type_paces WHERE workout_kind_id = ?`, workoutKindID)
|
||||
p, err := scanWorkoutTypePace(row)
|
||||
if err == sql.ErrNoRows {
|
||||
return WorkoutTypePace{WorkoutKindID: workoutKindID}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return WorkoutTypePace{}, fmt.Errorf("get workout type pace for kind %d: %w", workoutKindID, err)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// UpdateWorkoutTypePace overwrites the pace/zone row for one workout kind.
|
||||
func (db *DB) UpdateWorkoutTypePace(ctx context.Context, p WorkoutTypePace) error {
|
||||
_, err := db.ExecContext(ctx, `
|
||||
UPDATE workout_type_paces SET pace_min_sec_per_km=?, pace_max_sec_per_km=?, expected_hr_zone=?
|
||||
WHERE workout_kind_id=?`,
|
||||
p.PaceMinSecPerKm, p.PaceMaxSecPerKm, p.ExpectedHRZone, p.WorkoutKindID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update workout type pace for kind %d: %w", p.WorkoutKindID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListWorkoutTypePaces returns every workout kind's pace/zone row.
|
||||
func (db *DB) ListWorkoutTypePaces(ctx context.Context) ([]WorkoutTypePace, error) {
|
||||
rows, err := db.QueryContext(ctx, `SELECT `+workoutTypePaceColumns+` FROM workout_type_paces ORDER BY workout_kind_id`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list workout type paces: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
paces := []WorkoutTypePace{}
|
||||
for rows.Next() {
|
||||
p, err := scanWorkoutTypePace(rows)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("scan workout type pace row: %w", err)
|
||||
}
|
||||
paces = append(paces, p)
|
||||
}
|
||||
return paces, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user