Files
rideaware-api/internal/workout/zwo_parser.go

169 lines
4.1 KiB
Go

package workout
import (
"encoding/xml"
"errors"
"fmt"
)
type ParsedWorkoutData struct {
Name string `json:"name"`
Description string `json:"description"`
Author string `json:"author"`
SportType string `json:"sport_type"`
TotalDuration int `json:"total_duration"`
Segments []WorkoutSegment `json:"segments"`
}
type zwoFile struct {
Author string `xml:"author,attr"`
Name string `xml:"name,attr"`
Description string `xml:"description,attr"`
SportType string `xml:"sportType,attr"`
Workout zwoWorkout `xml:"workout"`
}
type zwoWorkout struct {
Warmups []zwoWarmup `xml:"Warmup"`
SteadyStates []zwoSteadyState `xml:"SteadyState"`
Cooldowns []zwoCooldown `xml:"Cooldown"`
Intervals []zwoInterval `xml:"Interval"`
Ramps []zwoRamp `xml:"Ramp"`
FreeRides []zwoFreeRide `xml:"FreeRide"`
}
type zwoWarmup struct {
Duration int `xml:"Duration,attr"`
PowerLow float64 `xml:"PowerLow,attr"`
PowerHigh float64 `xml:"PowerHigh,attr"`
Cadence int `xml:"Cadence,attr"`
}
type zwoSteadyState struct {
Duration int `xml:"Duration,attr"`
Power float64 `xml:"Power,attr"`
Cadence int `xml:"Cadence,attr"`
}
type zwoCooldown struct {
Duration int `xml:"Duration,attr"`
PowerLow float64 `xml:"PowerLow,attr"`
PowerHigh float64 `xml:"PowerHigh,attr"`
Cadence int `xml:"Cadence,attr"`
}
type zwoInterval struct {
Duration int `xml:"Duration,attr"`
PowerLow float64 `xml:"PowerLow,attr"`
PowerHigh float64 `xml:"PowerHigh,attr"`
Cadence int `xml:"Cadence,attr"`
}
type zwoRamp struct {
Duration int `xml:"Duration,attr"`
PowerLow float64 `xml:"PowerLow,attr"`
PowerHigh float64 `xml:"PowerHigh,attr"`
Cadence int `xml:"Cadence,attr"`
}
type zwoFreeRide struct {
Duration int `xml:"Duration,attr"`
Cadence int `xml:"Cadence,attr"`
}
func ParseZWO(content []byte) (*ParsedWorkoutData, error) {
var zwo zwoFile
err := xml.Unmarshal(content, &zwo)
if err != nil {
return nil, fmt.Errorf("failed to parse ZWO file: %w", err)
}
if zwo.Name == "" {
return nil, errors.New("workout name is required")
}
parsed := &ParsedWorkoutData{
Name: zwo.Name,
Description: zwo.Description,
Author: zwo.Author,
SportType: zwo.SportType,
Segments: []WorkoutSegment{},
}
// Parse warmups
for _, w := range zwo.Workout.Warmups {
seg := WorkoutSegment{
Type: "warmup",
Duration: w.Duration,
PowerLow: w.PowerLow,
PowerHigh: w.PowerHigh,
Cadence: w.Cadence,
}
parsed.Segments = append(parsed.Segments, seg)
parsed.TotalDuration += w.Duration
}
// Parse steady states
for _, s := range zwo.Workout.SteadyStates {
seg := WorkoutSegment{
Type: "steadystate",
Duration: s.Duration,
Power: s.Power,
Cadence: s.Cadence,
}
parsed.Segments = append(parsed.Segments, seg)
parsed.TotalDuration += s.Duration
}
// Parse cooldowns
for _, c := range zwo.Workout.Cooldowns {
seg := WorkoutSegment{
Type: "cooldown",
Duration: c.Duration,
PowerLow: c.PowerLow,
PowerHigh: c.PowerHigh,
Cadence: c.Cadence,
}
parsed.Segments = append(parsed.Segments, seg)
parsed.TotalDuration += c.Duration
}
// Parse intervals
for _, i := range zwo.Workout.Intervals {
seg := WorkoutSegment{
Type: "interval",
Duration: i.Duration,
PowerLow: i.PowerLow,
PowerHigh: i.PowerHigh,
Cadence: i.Cadence,
}
parsed.Segments = append(parsed.Segments, seg)
parsed.TotalDuration += i.Duration
}
// Parse ramps
for _, r := range zwo.Workout.Ramps {
seg := WorkoutSegment{
Type: "ramp",
Duration: r.Duration,
PowerLow: r.PowerLow,
PowerHigh: r.PowerHigh,
Cadence: r.Cadence,
}
parsed.Segments = append(parsed.Segments, seg)
parsed.TotalDuration += r.Duration
}
// Parse free rides
for _, f := range zwo.Workout.FreeRides {
seg := WorkoutSegment{
Type: "freeride",
Duration: f.Duration,
Cadence: f.Cadence,
}
parsed.Segments = append(parsed.Segments, seg)
parsed.TotalDuration += f.Duration
}
return parsed, nil
}