Last active
January 1, 2019 09:13
-
-
Save kenprice/d9a4c1c2cba5a7ae5465cce8ce8ad48f to your computer and use it in GitHub Desktop.
fitnotes-graph
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Given FitNotes CSV export, plot estimated 1RM | |
# | |
# Usage: fitnotes_graph.py <FILE> | |
# <FILE> is exported csv | |
# Assumes format: | |
# ['Date', 'Exercise', 'Category', 'Weight (lbs)', 'Reps', 'Distance', | |
# 'Distance Unit', 'Time', 'Comment'] | |
import csv | |
from datetime import datetime | |
from dateutil.relativedelta import relativedelta | |
import itertools | |
import matplotlib.pyplot as plt | |
import os | |
import sys | |
filepath = sys.argv[1] | |
# Structure | |
# categories = { | |
# "Legs": { | |
# "Standing Calf Raise": [ ... ], ... | |
# }, ... | |
# } | |
categories = {} | |
# 1RM Estimate Table: | |
brzycki = dict(((1, .100), (2, .95), (3, .90), (4, .88), (5, .86), (6, .83), \ | |
(7, .80), (8, .78), (9, .76), (10, .75), (11, .72), (12, .70))) | |
# Populate categories of exercises | |
with open(filepath) as f: | |
fitnotes = csv.reader(f, delimiter=',') | |
for i, row in enumerate(fitnotes): | |
if i == 0: | |
continue | |
date, exercise, category, weight, reps, distance, dunit, \ | |
time, comment = row | |
# Skip exercises with no weights (like ab crunches) | |
if float(weight) == 0.0: | |
continue | |
if category not in categories: | |
categories[category] = {} | |
exercises = categories[category] | |
if exercise not in exercises: | |
exercises[exercise] = [] | |
reps = int(reps) | |
one_rm = None | |
# Compute 1RM estimate using brzycki table | |
if reps in brzycki: | |
one_rm = int(float(weight) / brzycki[reps]) | |
else: | |
# Approx. 1RM beyond 12 reps by using value for 12 reps | |
one_rm = int(float(weight) / brzycki[12]) | |
date = datetime.strptime(date, "%Y-%m-%d") | |
# If there's already an exercise recorded for same date, | |
# then keep best rep (want unique dates for each exercise) | |
# Assumes CSV file is sorted by date | |
if len(exercises[exercise]) > 0 and \ | |
exercises[exercise][-1][0] == date: | |
if reps > exercises[exercise][-1][2]: | |
exercises[exercise].pop() | |
exercises[exercise].append( \ | |
(date, weight, reps, one_rm)) | |
else: | |
exercises[exercise].append( \ | |
(date, weight, reps, one_rm)) | |
def saveExercisePlot(category, exercise): | |
print(category, exercise) | |
x = [item[0] for item in categories[category][exercise]] | |
y = [item[3] for item in categories[category][exercise]] | |
plt.plot(x, y, linestyle='--') | |
plt.gcf().autofmt_xdate() | |
def datespan(startDate, endDate, delta): | |
currentDate = startDate | |
while currentDate < endDate: | |
yield currentDate | |
currentDate += delta | |
month_delta = relativedelta(months=+1) | |
x_month = [] | |
y_month = [] | |
# Kind of a brute-force way to chunk data by month - want to get | |
# best 1RM by month | |
for x_date in datespan(x[0], x[-1] + month_delta, delta=month_delta): | |
print (x_date) | |
best_1rm = 0 | |
best_date = None | |
for item in categories[category][exercise]: | |
if item[3] is None: | |
continue | |
if item[0].month == x_date.month and item[0].year == x_date.year: | |
if best_1rm < item[3]: | |
best_1rm = item[3] | |
best_date = item[0] | |
if best_date is not None: | |
x_month.append(best_date) | |
y_month.append(best_1rm) | |
if len(x_month) > 0: | |
plt.plot(x_month, y_month, marker='o') | |
category_name = ''.join(e for e in category if e.isalnum()) | |
if not os.path.exists("out/" + category_name): | |
os.makedirs("out/" + category_name) | |
exercise_name = ''.join(e for e in exercise if e.isalnum()) | |
plt.savefig("out/" + category_name + "/" + exercise_name + ".png") | |
plt.gcf().clear() | |
if not os.path.exists("out"): | |
os.makedirs("out") | |
for c_key in categories.keys(): | |
for e_key in categories[c_key].keys(): | |
saveExercisePlot(c_key, e_key) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment