132378de1c
This is version 0.2.1 created locally. This version is fully functioning with all features. Only having Scaling issues.
347 lines
11 KiB
Python
347 lines
11 KiB
Python
import tkinter
|
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
|
|
from matplotlib.figure import Figure
|
|
import requests
|
|
from scipy.signal import medfilt
|
|
from copy import deepcopy
|
|
from json import loads
|
|
|
|
import numpy as np
|
|
|
|
with open('endomondo.config', 'r') as conf:
|
|
username = conf.readline()[:-1]
|
|
password = conf.readline()
|
|
|
|
|
|
class Requester:
|
|
def __init__(self, email, password):
|
|
self.email = email
|
|
self.password = password
|
|
self.session = requests.session()
|
|
self.cookies = {}
|
|
self.headers = {
|
|
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0',
|
|
'Accept': 'application/json, text/plain, */*',
|
|
'Accept-Language': 'en-US,en;q=0.5',
|
|
'Referer': 'https://www.endomondo.com/home',
|
|
'Connection': 'keep-alive',
|
|
'Upgrade-Insecure-Requests': '1',
|
|
'Cache-Control': 'max-age=0',
|
|
'TE': 'Trailers',
|
|
}
|
|
self.data = '{"email":"' + self.email + '","password":"' + self.password + '","remember":true}'
|
|
|
|
def login(self):
|
|
# getting csrf token, jsessionid and awselb
|
|
response = self.session.get('https://www.endomondo.com/', headers=self.headers, cookies=self.cookies)
|
|
self.headers["X-CSRF-TOKEN"] = response.cookies["CSRF_TOKEN"]
|
|
self.headers["Referer"] = "https://www.endomondo.com/login"
|
|
self.headers["Origin"] = "https://www.endomondo.com"
|
|
self.headers["Content-Type"] = "application/json;charset=utf-8"
|
|
response2 = self.session.post('https://www.endomondo.com/rest/session', headers=self.headers,
|
|
cookies=self.cookies, data=self.data)
|
|
|
|
def get_workout(self, url):
|
|
self.headers["Referer"] = url
|
|
response = self.session.get("https://www.endomondo.com/rest/v1/" + url[26:], headers=self.headers,
|
|
cookies=self.cookies)
|
|
return response.content.decode('utf-8')
|
|
|
|
|
|
class Training:
|
|
class Plot:
|
|
def __init__(self, data, y_label, line_color):
|
|
self.raw_data = data
|
|
self.data = data
|
|
self.y_label = y_label
|
|
self.line_color = line_color
|
|
self.visible = True
|
|
self.inherited = None
|
|
|
|
def set_visible(self, boolean):
|
|
self.visible = boolean
|
|
|
|
def average(self, _i):
|
|
self.data = medfilt(self.raw_data, _i)
|
|
|
|
def __init__(self, json, line_type):
|
|
self.decoded = loads(json)
|
|
self.line_type = line_type
|
|
self.name = self.decoded['id']
|
|
|
|
# creating all plots
|
|
heart_rate = []
|
|
for i in range(len(self.decoded['points']['points'])):
|
|
if "heart_rate" in self.decoded['points']['points'][i]['sensor_data']:
|
|
heart_rate.append(self.decoded['points']['points'][i]['sensor_data']['heart_rate'])
|
|
else:
|
|
if heart_rate:
|
|
heart_rate.append(heart_rate[-1])
|
|
else:
|
|
heart_rate.append(0)
|
|
self.plot_heart_rate = self.Plot(heart_rate, "[heart rate] = bpm", 'tab:red')
|
|
|
|
self.distance = [self.decoded["points"]["points"][i]["distance"]
|
|
for i in range(len(self. decoded['points']['points']))]
|
|
|
|
speed = []
|
|
for i in range(len(self.decoded['points']['points'])):
|
|
if "speed" in self.decoded['points']['points'][i]['sensor_data']:
|
|
speed.append(self.decoded['points']['points'][i]['sensor_data']['speed'])
|
|
else:
|
|
if speed:
|
|
speed.append(speed[-1])
|
|
else:
|
|
speed.append(0)
|
|
avg = np.average(speed)
|
|
for j, i in enumerate(speed):
|
|
try:
|
|
speed[j] = 60 / i
|
|
except ZeroDivisionError:
|
|
speed[j] = 60 / avg
|
|
self.plot_speed = self.Plot(speed, "[speed] = minpkm", 'tab:blue')
|
|
|
|
alt = []
|
|
for i in range(len(self.decoded['points']['points'])):
|
|
if "altitude" in self.decoded['points']['points'][i]:
|
|
alt.append(self.decoded['points']['points'][i]['altitude'])
|
|
else:
|
|
if alt:
|
|
alt.append(alt[-1])
|
|
else:
|
|
alt.append(None)
|
|
for j, i in enumerate(alt):
|
|
if i is None:
|
|
for el in alt[j:]:
|
|
if el:
|
|
alt[j] = el
|
|
self.plot_altitude = self.Plot(alt, "[altitude] = m", 'tab:green')
|
|
self.plot_speed.inherited = [self.distance, self.line_type, self.name]
|
|
self.plot_altitude.inherited = [self.distance, self.line_type, self.name]
|
|
self.plot_heart_rate.inherited = [self.distance, self.line_type, self.name]
|
|
self.date = self.decoded["local_start_time"]
|
|
self.empty_plot = self.Plot([0 for _ in range(len(self.decoded['points']['points']))], "", "tab:blue")
|
|
self.empty_plot.set_visible(False)
|
|
self.empty_plot.inherited = [self.distance, self.line_type, self.name]
|
|
|
|
|
|
def txt_changed_0(txt):
|
|
if len(txt) >= 50:
|
|
Trainings[0] = Training(user.get_workout(txt), '-')
|
|
plot(states)
|
|
|
|
|
|
def txt_changed_1(txt):
|
|
if not txt:
|
|
Trainings.pop(1)
|
|
if len(txt) >= 50:
|
|
if len(Trainings) >= 2:
|
|
Trainings[1] = Training(user.get_workout(txt), '--')
|
|
else:
|
|
Trainings.append(Training(user.get_workout(txt), '--'))
|
|
plot(states)
|
|
|
|
|
|
def txt_changed_2(txt):
|
|
if not txt:
|
|
Trainings.pop(2)
|
|
if len(txt) >= 50:
|
|
if len(Trainings) >= 3:
|
|
Trainings[2] = Training(user.get_workout(txt), ':')
|
|
else:
|
|
Trainings.append(Training(user.get_workout(txt), ':'))
|
|
plot(states)
|
|
|
|
|
|
def txt_changed_3(txt):
|
|
if not txt:
|
|
Trainings.pop(3)
|
|
if len(txt) >= 50:
|
|
if len(Trainings) >= 4:
|
|
Trainings[3] = Training(user.get_workout(txt), '-.')
|
|
else:
|
|
Trainings.append(Training(user.get_workout(txt), '-.'))
|
|
plot(states)
|
|
|
|
|
|
def slide(numb):
|
|
numb = int(numb)
|
|
for training in Trainings:
|
|
training.plot_speed.average(numb)
|
|
training.plot_heart_rate.average(numb)
|
|
plot(states)
|
|
|
|
|
|
def slide_change(n):
|
|
n = int(n)
|
|
if not n % 2:
|
|
slider.set(n + 1)
|
|
|
|
|
|
def btn_slide():
|
|
slide(int(slider.get()))
|
|
|
|
|
|
def check_box():
|
|
global states
|
|
states['plot_speed'] = varSpeed.get()
|
|
states['plot_altitude'] = varAltitude.get()
|
|
states['plot_heart_rate'] = varHeart.get()
|
|
plot(states)
|
|
|
|
|
|
def submit():
|
|
global txtBoxes
|
|
temp = [txtBox0.get(), txtBox1.get(), txtBox2.get(), txtBox3.get()]
|
|
for i, element in enumerate(temp):
|
|
if not element == txtBoxes[i]:
|
|
if i == 0:
|
|
txt_changed_0(element)
|
|
if i == 1:
|
|
txt_changed_1(element)
|
|
if i == 2:
|
|
txt_changed_2(element)
|
|
if i == 3:
|
|
txt_changed_3(element)
|
|
txtBoxes[i] = element
|
|
|
|
|
|
user = Requester(username, password)
|
|
user.login()
|
|
Trainings = [Training(user.get_workout("https://www.endomondo.com/users/19154541/workouts/1458780940"), '-')]
|
|
|
|
|
|
states = {'plot_speed': True, 'plot_altitude': True, 'plot_heart_rate': False}
|
|
|
|
txtBoxes = ['', '', '', '']
|
|
|
|
root = tkinter.Tk()
|
|
root.wm_title("Endomondo Analyzer")
|
|
|
|
fig = Figure(figsize=(5, 4), dpi=100)
|
|
fig.add_subplot(111)
|
|
ax1 = fig.subplots()
|
|
ax2 = ax1.twinx()
|
|
|
|
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
|
|
canvas.draw()
|
|
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
|
|
|
|
toolbar = NavigationToolbar2Tk(canvas, root)
|
|
toolbar.update()
|
|
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
|
|
|
|
txtBox0 = tkinter.Entry(root)
|
|
txtBox0.place(x=20, y=40)
|
|
lbl0 = tkinter.Label(root, text="durchgezogen")
|
|
lbl0.place(x=200, y=40)
|
|
lbl1 = tkinter.Label(root, text="gestrichelt")
|
|
lbl1.place(x=200, y=80)
|
|
lbl0 = tkinter.Label(root, text="gepunktet")
|
|
lbl0.place(x=200, y=120)
|
|
lbl1 = tkinter.Label(root, text="gestrichpunktet")
|
|
lbl1.place(x=200, y=160)
|
|
txtBox1 = tkinter.Entry(root)
|
|
txtBox1.place(x=20, y=80)
|
|
txtBox2 = tkinter.Entry(root)
|
|
txtBox2.place(x=20, y=120)
|
|
txtBox3 = tkinter.Entry(root)
|
|
txtBox3.place(x=20, y=160)
|
|
|
|
|
|
slider = tkinter.Scale(root, from_=1, to=35, orient=tkinter.HORIZONTAL, length=300, command=slide_change)
|
|
slider.place(x=20, y=250)
|
|
|
|
varSpeed = tkinter.BooleanVar()
|
|
chckSpeed = tkinter.Checkbutton(root, text="Speed", command=check_box, variable=varSpeed)
|
|
chckSpeed.place(x=20, y=410)
|
|
chckSpeed.select()
|
|
varAltitude = tkinter.BooleanVar()
|
|
chckAltitude = tkinter.Checkbutton(root, text="Altitude", command=check_box, variable=varAltitude)
|
|
chckAltitude.place(x=20, y=450)
|
|
chckAltitude.select()
|
|
varHeart = tkinter.BooleanVar()
|
|
chckHeart = tkinter.Checkbutton(root, text="Heart Rate", command=check_box, variable=varHeart)
|
|
chckHeart.place(x=20, y=490)
|
|
|
|
btnSubmit = tkinter.Button(root, command=submit, text="Submit")
|
|
btnSubmit.place(x=20, y=200)
|
|
|
|
|
|
btnChangeScale = tkinter.Button(root, command=btn_slide, text="Change Average")
|
|
btnChangeScale.place(x=20, y=290)
|
|
|
|
Dates = tkinter.StringVar(root)
|
|
lblDates = tkinter.Label(root, textvariable=Dates)
|
|
lblDates.place(x=20, y=550)
|
|
|
|
|
|
def _quit():
|
|
root.quit() # stops mainloop
|
|
root.destroy() # this is necessary on Windows to prevent
|
|
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
|
|
|
|
|
|
def plot(_dict):
|
|
# choosing which plots to show on which axis
|
|
plot1 = None
|
|
plot2 = None
|
|
dictionary = deepcopy(_dict)
|
|
for element in dictionary:
|
|
if dictionary[element]:
|
|
plot1 = element
|
|
dictionary[element] = 0
|
|
break
|
|
for element in dictionary:
|
|
if dictionary[element]:
|
|
plot2 = element
|
|
dictionary[element] = 0
|
|
break
|
|
global ax1
|
|
global ax2
|
|
color = ""
|
|
ax1.clear()
|
|
ax2.clear()
|
|
# defining lists of plots
|
|
if plot1 == "plot_speed":
|
|
plots1 = [i.plot_speed for i in Trainings]
|
|
elif plot1 == "plot_altitude":
|
|
plots1 = [i.plot_altitude for i in Trainings]
|
|
elif plot1 == "plot_heart_rate":
|
|
plots1 = [i.plot_heart_rate for i in Trainings]
|
|
if plot2 == "plot_altitude":
|
|
plots2 = [i.plot_altitude for i in Trainings]
|
|
elif plot2 == "plot_heart_rate":
|
|
plots2 = [i.plot_heart_rate for i in Trainings]
|
|
if plot1 is None:
|
|
plots1 = [Trainings[0].empty_plot]
|
|
if plot2 is None:
|
|
plots2 = [Trainings[0].empty_plot]
|
|
color = plots1[0].line_color
|
|
ax1.set_xlabel('[distance] = km')
|
|
ax1.set_ylabel(plots1[0].y_label, color=color)
|
|
dates = "Dates:\n"
|
|
for training in Trainings:
|
|
dates = dates + training.date[:10] + "\n"
|
|
Dates.set(dates)
|
|
|
|
for pl in plots1:
|
|
ax1.plot(pl.inherited[0], pl.data, color=color, visible=pl.visible, linestyle=pl.inherited[1])
|
|
ax1.tick_params(axis='y', labelcolor=color)
|
|
color = plots2[0].line_color
|
|
ax2.set_xlabel('[distance] = km')
|
|
ax2.set_ylabel(plots2[0].y_label, color=color)
|
|
|
|
for pl in plots2:
|
|
ax2.plot(pl.inherited[0], pl.data, color=color, visible=pl.visible, linestyle=pl.inherited[1])
|
|
ax2.tick_params(axis='y', labelcolor=color)
|
|
fig.subplots_adjust(left=0.2)
|
|
fig.canvas.draw_idle()
|
|
|
|
|
|
plot(states)
|
|
fig.canvas.draw_idle()
|
|
button = tkinter.Button(master=root, text="Quit", command=_quit)
|
|
button.pack(side=tkinter.BOTTOM)
|
|
|
|
tkinter.mainloop() |