|
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() |