Skip to content

Instantly share code, notes, and snippets.

@jaimergp
Created January 22, 2024 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaimergp/3d5a858a2d09954a834cc193b5f3766e to your computer and use it in GitHub Desktop.
Save jaimergp/3d5a858a2d09954a834cc193b5f3766e to your computer and use it in GitHub Desktop.
Zero-to-hero to Hevy

From Zero To Hero to Hevy

I had been using Zero To Hero for years to track my workouts. The app has not seen development in a while.

You can backup your ZTH data to a Realm database via the app. This .zth file can be exported to a JSON with the attached Node.js script (you need npm install realm).

Then the JSON can be post-processed into a CSV file following the Strong app format (I took this sample).

The resulting CSV can be imported via the Hevy Import/Export menu.

import csv
import json
from datetime import datetime, timedelta
from functools import lru_cache
def read_json(path):
with open(path, "r") as f:
return json.load(f)
def write_csv(path, data):
with open(path, "w") as f:
writer = csv.writer(f)
writer.writerows(data)
def populate_csv_from_json(data):
rows = [
[
"Date",
"Workout Name",
"Duration",
"Exercise Name",
"Set Order",
"Weight",
"Reps",
"Distance",
"Seconds",
"Notes",
"Workout Notes",
"RPE",
]
]
class Duration:
def __init__(self, *timestamps):
self.timestamps = list(timestamps)
def append(self, timestamp):
if timestamp:
self.timestamps.append(timestamp)
def __str__(self):
dt = timedelta(milliseconds = max(self.timestamps) - min(self.timestamps))
mm, ss = divmod(dt.total_seconds(), 60)
hh, mm = divmod(mm, 60)
if hh > 1:
hh = 0
if hh == 0 and mm == 0:
return "1h 00m"
return "%dh %02dm" % (hh, mm)
for workout in data["Workout"]:
timestamp = workout["date"] or 0
date = datetime.fromtimestamp(timestamp / 1000).strftime("%Y-%m-%d %H:%M:%S")
workout_name = workout["name"]
duration = Duration(timestamp)
for exercise in workout["exerciseList"]:
for set_order, movement in enumerate(exercise["movementSetList"], 1):
row = []
duration.append(movement["date"])
exercise_name = movement_map(movement["movement"])
weight = movement["performedWeight"]
reps = movement["reps"]
row.extend(
[
date,
workout_name,
duration,
exercise_name,
set_order,
weight,
reps,
"",
"",
"",
"",
"",
]
)
rows.append(row)
for workout in data["CustomProgramData"]:
...
# for exercise in data["Exercise"]:
# ...
return rows
@lru_cache(maxsize=None)
def movement_map(key):
return {
"1653663211431": "Butterfly reverse",
"1693643373232": "Push-up",
"1693645906260": "Pike push-ups",
"BARBELLROW": "Barbell row",
"BEHINDTHENECKPRESS": "Behind the neck press",
"BENCHPRESS": "Bench press",
"BENCHPRESS_DECLINE": "Declined bench press",
"BENCHPRESS_INCLINE": "Inclined bench press",
"BENCHPRESS_INCLINE_DUMBBELL": "Inclined bench press (dumbbell)",
"BICEPS_CURL_HAMMER_DUMBBELL": "Biceps hammer curl (dumbbell)",
"CALFRAISE": "Seated calf raise",
"CALFRAISE_STANDING": "Standing calf raise",
"CURL_BARBELL": "Barbell curl",
"CURL_DUMBBELL": "Dumbbell curl",
"DEADLIFT": "Deadlift",
"DEADLIFT_ROMANIAN": "Romanian deadlift",
"FLYS_DUMBBELL": "Dumbbell fly",
"FLYS_INCLINE_CABLE": "Incline cable fly",
"LEGPRESS": "Leg press",
"LEG_CURL": "Leg curl",
"LEG_EXTENSION": "Leg extension",
"LUNGE_BACKSTEP": "Backstep lunge",
"OVERHEADPRESS": "Overhead press",
"PEC_FLY_MACHINE": "Pec fly machine",
"PULLDOWN_LATERAL": "Lateral pull down",
"PULLUP": "Pull-up",
"PULL_FACE": "Face pull",
"ROW_CABLE_SEATED": "Seated cable row",
"ROW_DUMBBELL": "Row (dumbbell)",
"ROW_PENDLAY": "Row (pendlay)",
"SHOULDER_PRESS_SEATED_DUMBBELL": "Seated shoulder press (dumbbell)",
"SHOULDER_RAISE_SIDE_LATERAL": "Lateral side raise",
"SKULLCRUSHERS": "Skullcrushers",
"SQUAT": "Squat",
"SQUAT_FRONT": "Front squat",
"TRICEPS_DIPS": "Dips",
"TRICEP_EXTENSION_CABLE": "Cable tricep extension",
"TRICEP_PUSHDOWNS": "Tricep pushdowns",
}.get(key, key)
def main():
data = read_json("exported_data.json")
rows = populate_csv_from_json(data)
write_csv("exported_data.csv", rows)
if __name__ == "__main__":
main()
const Realm = require('realm');
const fs = require('fs');
// Function to export entire Realm database to JSON
async function exportRealmToJSON() {
// Specify the file path for your Realm database
const realmFile = './backup.zth';
// Configure Realm with the specified file path
const realm = await Realm.open({
path: realmFile,
readOnly: true,
});
// Get all objects from all available object types
const allObjects = {};
realm.schema.forEach((objectType) => {
allObjects[objectType.name] = realm.objects(objectType.name);
});
// Convert to JSON
const jsonData = JSON.stringify(allObjects, null, 2);
// Print or save the JSON string as needed
console.log(jsonData);
// If you want to save to a file
const exportFilePath = 'exported_data.json';
fs.writeFileSync(exportFilePath, jsonData, 'utf-8');
// Close the Realm instance
realm.close();
}
// Call the function to export
exportRealmToJSON();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment