Files

347 lines
11 KiB
Python
Executable File

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=150, y=40)
lbl1 = tkinter.Label(root, text="gestrichelt")
lbl1.place(x=150, y=80)
lbl0 = tkinter.Label(root, text="gepunktet")
lbl0.place(x=150, y=120)
lbl1 = tkinter.Label(root, text="gestrichpunktet")
lbl1.place(x=150, 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=580)
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=540)
Dates = tkinter.StringVar(root)
lblDates = tkinter.Label(root, textvariable=Dates)
lblDates.place(x=20, y=250)
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()