Skip to content

Build Hevy CSV converter #74

Description

@radupana

Overview

Implement a Hevy CSV to openweight converter as part of the converter architecture (#73).

Hevy is the second-most popular strength training app and has the richest CSV export of any competitor — it includes RPE, set types, supersets, and exercise notes. This makes it an excellent second converter to validate the architecture, exercising code paths that Strong's simpler format does not.

Hevy CSV Format

Known columns

Column Type Maps to Notes
title string WorkoutLog.name Workout name
start_time ISO 8601 datetime WorkoutLog.date Workout start time
end_time ISO 8601 datetime (derive durationSeconds) Workout end time
description string WorkoutLog.notes Workout-level notes
exercise_title string ExerciseLog.exercise.name Exercise name
superset_id integer or empty ExerciseLog.supersetId Groups exercises into supersets
exercise_notes string ExerciseLog.notes Per-exercise notes
set_index integer SetLog ordering 1-based set order within exercise
set_type string SetLog.type See mapping below
weight_lbs number SetLog.weight + unit conversion Always in lbs regardless of user setting
reps integer SetLog.reps Rep count
distance_miles number SetLog.distance + unit conversion Always in miles
duration_seconds integer SetLog.durationSeconds For timed exercises
rpe number SetLog.rpe 0-10 scale

Set type mapping

Hevy value openweight SetLog.type
Normal "working"
Warm Up "warmup"
Failure "failure"
Drop Set "dropset"

Known quirks

  • Weight is always exported in lbs regardless of the user's in-app unit preference. Converter should offer --unit kg to convert to kg on output.
  • Distance is always in miles.
  • superset_id is empty string (not null) when not in a superset.
  • end_time minus start_time gives workout duration.
  • Hevy can also import Strong CSV format, so its format is partially influenced by Strong's.

Implementation

Hevy-specific transformer (tools/converters/src/converters/hevy.ts)

const HEVY_COLUMNS: ColumnMap = {
  "title":             "workoutName",
  "start_time":        "date",
  "end_time":          "endTime",
  "description":       "workoutNotes",
  "exercise_title":    "exerciseName",
  "superset_id":       "supersetId",
  "exercise_notes":    "exerciseNotes",
  "set_index":         "setOrder",
  "set_type":          "setType",
  "weight_lbs":        "weight",
  "reps":              "reps",
  "distance_miles":    "distance",
  "duration_seconds":  "durationSeconds",
  "rpe":               "rpe",
};

What Hevy exercises that Strong does not

  • Superset grouping (supersetId mapping)
  • RPE (direct mapping, no conversion needed)
  • Set types (4 types vs Strong's none)
  • Unit conversion (lbs-only export needs kg option)
  • Duration calculation (end_time - start_time)
  • Distance conversion (miles to m/km/ft/mi/yd)

Conversion report specifics

  • Report should note that weights were converted from lbs if --unit kg was used
  • Report should note distance conversion from miles
  • Report should flag any unrecognized set types

Test Plan

  • Fixture-based tests with sample Hevy CSV data
  • Verify superset grouping produces correct supersetId values
  • Verify RPE mapping
  • Verify set type mapping for all 4 types
  • Verify unit conversion (lbs to kg)
  • Verify duration calculation from start/end times
  • Verify distance conversion
  • Roundtrip: converted output passes validateWorkoutLog()
  • Edge cases: workouts with no supersets, exercises with no RPE, timed-only exercises

Depends On

Part Of

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions