Show expected vs actual pace/HR per lap in the Review Queue
Activities recorded from a structured Garmin workout carry a workoutId; when its steps line up 1:1 with the recorded laps, the per-step target pace/HR zone is resolved (via a Karvonen lookup for named zones) and stored on each lap. The Review Queue plots it against the actual per-lap pace/HR so the user can eyeball whether a run matches its plan while sorting it.
This commit is contained in:
@@ -36,6 +36,8 @@ type Client interface {
|
||||
GetActivitySplits(ctx context.Context, activityID int64) (ActivitySplits, error)
|
||||
// GetActivityDetails fetches raw per-second telemetry for one activity.
|
||||
GetActivityDetails(ctx context.Context, activityID int64) (ActivityDetails, error)
|
||||
// GetWorkoutByID fetches a structured workout's step-by-step plan.
|
||||
GetWorkoutByID(ctx context.Context, workoutID int64) (Workout, error)
|
||||
// Close terminates the subprocess, if running.
|
||||
Close() error
|
||||
}
|
||||
@@ -270,6 +272,27 @@ func (c *mcpClient) GetActivityDetails(ctx context.Context, activityID int64) (A
|
||||
return details, nil
|
||||
}
|
||||
|
||||
func (c *mcpClient) GetWorkoutByID(ctx context.Context, workoutID int64) (Workout, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if err := c.ensureStarted(ctx); err != nil {
|
||||
return Workout{}, err
|
||||
}
|
||||
msg, err := c.callTool(ctx, "get_workout_by_id", map[string]any{
|
||||
"workout_id": strconv.FormatInt(workoutID, 10),
|
||||
})
|
||||
if err != nil {
|
||||
return Workout{}, err
|
||||
}
|
||||
|
||||
var workout Workout
|
||||
if err := json.Unmarshal([]byte(msg), &workout); err != nil {
|
||||
return Workout{}, fmt.Errorf("get_workout_by_id did not return valid JSON (%q): %w", truncate(msg, 200), err)
|
||||
}
|
||||
return workout, nil
|
||||
}
|
||||
|
||||
func (c *mcpClient) Close() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user