-
Notifications
You must be signed in to change notification settings - Fork 2
/
logparse.py
executable file
·186 lines (139 loc) · 5.33 KB
/
logparse.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/env python3
# vim: syntax=python ts=4 et sw=4 sts=4:
import datetime
import sys
from common import *
def error(text):
print("Error: " + text)
sys.exit(1)
#####################################################################
def from_kg(x):
return float(x) * 2.20462262
def weight2float(x):
# Used for chain notation.
if "+" in x:
sum = 0
for k in x.split('+'):
sum += weight2float(k)
return sum
if "kg" in x:
return from_kg(x.replace("kg",""))
return float(x)
# Returns the number of 2-space tabs on the line.
def get_indentation_level(line):
return (len(line) - len(line.lstrip())) >> 1
# Given text like "425x5x3" or "415x3@9", return a list of sets.
def makesets(text):
xcount = text.count('x')
# FIXME: For the moment, we'll just discard old pause notation.
text = text.replace('p', '')
# FIXME: Also some weirdo one-off "block" notation.
text = text.replace('b', '')
# If nothing special is noted, then it's probably a warmup set of 5.
if xcount == 0:
return [Set(weight2float(text), 5)]
# Single set, maybe with RPE or failure.
if xcount == 1 and not '(' in text:
# Check for failure and remove it from the string.
lowertext = text.lower()
failure = 'f' in lowertext
lowertext = lowertext.replace('f', '')
# Check for RPE and remove it from the string.
rpe = 0
if '@' in lowertext:
[lowertext, rpestr] = lowertext.split('@')
rpe = float(rpestr)
# Remaining string should just be weight x reps.
[weight, reps] = lowertext.split('x')
# Case of 200xf
if not reps:
reps = 0
return [Set(weight2float(weight), int(reps), rpe, failure)]
# Multiple sets with individual notation.
# With failure: 225x(5,5,4f)
# With RPE: 225x5@(7,8,9)
if xcount == 1 and '(' in text:
parens = text[text.index('(')+1 : -1].split(',')
text = text[0 : text.index('(')]
# List is across reps.
if text[-1] == 'x':
weight = text[0:-1]
sets = []
for k in parens:
failure = 'f' in k
reps = k.replace('f', '')
rpe = 0.0
# A set can have individual RPE notation:
# 45x(1,2,3,4@9)
if '@' in reps:
[reps, rpe] = reps.split('@')
if reps == '':
reps = 0
sets.append(Set(weight2float(weight), int(reps), float(rpe), failure))
return sets
# List is across RPE.
if text[-1] == '@':
[weight, reps] = text[0:-1].split('x')
return [Set(weight2float(weight), int(reps), float(x)) for x in parens]
# Multiple sets across without RPE.
# Failure is still sometimes denoted by "325x3fx3". Then all sets failed.
if xcount == 2:
failure = 'f' in text
text = text.replace('f', '')
[weight, reps, nsets] = text.split('x')
if reps == '': # Then there were no successful reps.
reps = 0
return [Set(weight2float(weight), int(reps), 0, failure) for x in range(0,int(nsets))]
error("Could not parse sets: " + text)
# Main log parser function.
def parse(filename):
# Open the file and remove all comments.
with open(filename) as fd:
lines = [x.split('#')[0].rstrip() for x in fd.readlines()]
exlog = []
# Zero'th level of indentation: dates.
# First level of indentation: lift or bodyweight.
# Second level of indentation: weights, reps, etc.
session = None
lift = None
for linenum, line in enumerate(lines):
if len(line) == 0:
continue
try:
level = get_indentation_level(line)
# New training session.
if level == 0:
[year, month, day] = [int(x) for x in (line.split()[0]).split('-')]
session = TrainingSession(year, month, day)
exlog.append(session)
# New lift for the current session, or bodyweight declaration.
elif level == 1:
name = line.lstrip().split(':')[0]
if name == "weight":
maybeweight = line.split(':')[1].strip()
if maybeweight:
session.setbodyweight(weight2float(maybeweight))
# The earliest entries do some jogging for warmup.
elif name == "warmup":
continue
else:
lift = Lift(name)
session.addlift(lift)
# List of sets for the current lift.
elif level == 2:
# Can't just split by comma, since a line may be "155x3, 175x(2,2,1)".
for x in line.split(', '):
sets = makesets(x.strip())
lift.addsets(sets)
except Exception as err:
print("Error on line %s (%s): %s" % (linenum+1, line, err))
sys.exit(1)
return exlog
if __name__ == '__main__':
exlog = parse(sys.argv[1])
try:
for session in exlog:
for lift in session.lifts:
print(str(session.date) + ' ' + lift.name + " " + str(lift.e1rm() or lift.epley()))
except BrokenPipeError:
pass