Merge branch 'feature/goldmedalscore' into develop
This commit is contained in:
+80
-14
@@ -8,6 +8,7 @@ from __future__ import unicode_literals
|
|||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
from rowers.models import (
|
from rowers.models import (
|
||||||
Workout, Team, CalcAgePerformance,C2WorldClassAgePerformance,
|
Workout, Team, CalcAgePerformance,C2WorldClassAgePerformance,
|
||||||
|
User
|
||||||
)
|
)
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
@@ -325,10 +326,13 @@ def workout_summary_to_df(
|
|||||||
startdate=datetime.datetime(1970,1,1),
|
startdate=datetime.datetime(1970,1,1),
|
||||||
enddate=timezone.now()+timezone.timedelta(days=1)):
|
enddate=timezone.now()+timezone.timedelta(days=1)):
|
||||||
|
|
||||||
ws = Workout.objects.filter(user=rower).order_by("startdatetime")
|
ws = Workout.objects.filter(
|
||||||
|
user=rower,date__gte=startdate,date__lte=enddate
|
||||||
|
).order_by("startdatetime")
|
||||||
|
|
||||||
types = []
|
types = []
|
||||||
names = []
|
names = []
|
||||||
|
ids = []
|
||||||
startdatetimes = []
|
startdatetimes = []
|
||||||
timezones = []
|
timezones = []
|
||||||
distances = []
|
distances = []
|
||||||
@@ -339,12 +343,19 @@ def workout_summary_to_df(
|
|||||||
notes = []
|
notes = []
|
||||||
tcx_links = []
|
tcx_links = []
|
||||||
csv_links = []
|
csv_links = []
|
||||||
|
workout_links = []
|
||||||
|
goldstandards = []
|
||||||
|
goldstandarddurations = []
|
||||||
rscores = []
|
rscores = []
|
||||||
|
hrtss = []
|
||||||
trimps = []
|
trimps = []
|
||||||
|
rankingpieces = []
|
||||||
|
boattypes = []
|
||||||
|
|
||||||
for w in ws:
|
for w in ws:
|
||||||
types.append(w.workouttype)
|
types.append(w.workouttype)
|
||||||
names.append(w.name)
|
names.append(w.name)
|
||||||
|
ids.append(encoder.encode_hex(w.id))
|
||||||
startdatetimes.append(w.startdatetime)
|
startdatetimes.append(w.startdatetime)
|
||||||
timezones.append(w.timezone)
|
timezones.append(w.timezone)
|
||||||
distances.append(w.distance)
|
distances.append(w.distance)
|
||||||
@@ -352,6 +363,7 @@ def workout_summary_to_df(
|
|||||||
weightcategories.append(w.weightcategory)
|
weightcategories.append(w.weightcategory)
|
||||||
adaptivetypes.append(w.adaptiveclass)
|
adaptivetypes.append(w.adaptiveclass)
|
||||||
weightvalues.append(w.weightvalue)
|
weightvalues.append(w.weightvalue)
|
||||||
|
boattypes.append(w.boattype)
|
||||||
notes.append(w.notes)
|
notes.append(w.notes)
|
||||||
tcx_link = SITE_URL+'/rowers/workout/{id}/emailtcx'.format(
|
tcx_link = SITE_URL+'/rowers/workout/{id}/emailtcx'.format(
|
||||||
id=encoder.encode_hex(w.id)
|
id=encoder.encode_hex(w.id)
|
||||||
@@ -361,25 +373,41 @@ def workout_summary_to_df(
|
|||||||
id=encoder.encode_hex(w.id)
|
id=encoder.encode_hex(w.id)
|
||||||
)
|
)
|
||||||
csv_links.append(csv_link)
|
csv_links.append(csv_link)
|
||||||
|
workout_link = SITE_URL+'/rowers/workout/{id}/'.format(
|
||||||
|
id=encoder.encode_hex(w.id)
|
||||||
|
)
|
||||||
|
workout_links.append(workout_link)
|
||||||
trimps.append(workout_trimp(w)[0])
|
trimps.append(workout_trimp(w)[0])
|
||||||
rscore = workout_rscore(w)
|
rscore = workout_rscore(w)
|
||||||
rscores.append(int(rscore[0]))
|
rscores.append(int(rscore[0]))
|
||||||
|
hrtss.append(int(w.hrtss))
|
||||||
|
goldstandard,goldstandardduration = workout_goldmedalstandard(w)
|
||||||
|
goldstandards.append(int(goldstandard))
|
||||||
|
goldstandarddurations.append(int(goldstandardduration))
|
||||||
|
rankingpieces.append(w.rankingpiece)
|
||||||
|
|
||||||
df = pd.DataFrame({
|
df = pd.DataFrame({
|
||||||
'name':names,
|
'ID': ids,
|
||||||
'date':startdatetimes,
|
'date':startdatetimes,
|
||||||
|
'name':names,
|
||||||
|
'link':workout_links,
|
||||||
'timezone':timezones,
|
'timezone':timezones,
|
||||||
'type':types,
|
'type':types,
|
||||||
|
'boat type':boattypes,
|
||||||
'distance (m)':distances,
|
'distance (m)':distances,
|
||||||
'duration ':durations,
|
'duration ':durations,
|
||||||
|
'ranking piece':rankingpieces,
|
||||||
'weight category':weightcategories,
|
'weight category':weightcategories,
|
||||||
'adaptive classification':adaptivetypes,
|
'adaptive classification':adaptivetypes,
|
||||||
'weight (kg)':weightvalues,
|
'weight (kg)':weightvalues,
|
||||||
'notes':notes,
|
|
||||||
'Stroke Data TCX':tcx_links,
|
'Stroke Data TCX':tcx_links,
|
||||||
'Stroke Data CSV':csv_links,
|
'Stroke Data CSV':csv_links,
|
||||||
'TRIMP Training Load':trimps,
|
'TRIMP Training Load':trimps,
|
||||||
'TSS Training Load':rscores,
|
'TSS Training Load':rscores,
|
||||||
|
'hrTSS Training Load':hrtss,
|
||||||
|
'GS':goldstandards,
|
||||||
|
'GS_secs':goldstandarddurations,
|
||||||
|
'notes':notes,
|
||||||
})
|
})
|
||||||
|
|
||||||
return df
|
return df
|
||||||
@@ -1026,28 +1054,58 @@ from rowers.datautils import p0
|
|||||||
from rowers.utils import calculate_age
|
from rowers.utils import calculate_age
|
||||||
from scipy import optimize
|
from scipy import optimize
|
||||||
|
|
||||||
def fitscore(rower,workout):
|
def get_workoutsummaries(userid,startdate):
|
||||||
|
u = User.objects.get(id=userid)
|
||||||
|
r = u.rower
|
||||||
|
df = workout_summary_to_df(r,startdate=startdate)
|
||||||
|
df.drop(['Stroke Data TCX','Stroke Data CSV'],axis=1,inplace=True)
|
||||||
|
df = df.sort_values('date',ascending=False)
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
def workout_goldmedalstandard(workout):
|
||||||
|
if workout.goldmedalstandard > 0:
|
||||||
|
return workout.goldmedalstandard,workout.goldmedalseconds
|
||||||
|
if workout.workouttype in rowtypes:
|
||||||
|
goldmedalstandard,goldmedalseconds = calculate_goldmedalstandard(workout.user,workout)
|
||||||
|
workout.goldmedalstandard = goldmedalstandard
|
||||||
|
workout.goldmedalseconds = goldmedalseconds
|
||||||
|
workout.save()
|
||||||
|
return goldmedalstandard, goldmedalseconds
|
||||||
|
else:
|
||||||
|
return 0,0
|
||||||
|
|
||||||
|
def calculate_goldmedalstandard(rower,workout):
|
||||||
cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
|
cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
|
||||||
try:
|
try:
|
||||||
df = pd.read_parquet(cpfile)
|
df = pd.read_parquet(cpfile)
|
||||||
except:
|
except:
|
||||||
df, delta, cpvalues = setcp(workout)
|
df, delta, cpvalues = setcp(workout)
|
||||||
|
|
||||||
|
if df.empty:
|
||||||
|
df, delta, cpvalues = setcp(workout)
|
||||||
|
|
||||||
age = calculate_age(rower.birthdate,today=workout.date)
|
age = calculate_age(rower.birthdate,today=workout.date)
|
||||||
|
|
||||||
agerecords = CalcAgePerformance.objects.filter(
|
agerecords = CalcAgePerformance.objects.filter(
|
||||||
age=age,
|
age=age,
|
||||||
sex=rower.sex,
|
sex=rower.sex,
|
||||||
weightcategory = rower.weightcategory
|
weightcategory = rower.weightcategory
|
||||||
)
|
)
|
||||||
|
|
||||||
wcdurations = []
|
wcdurations = []
|
||||||
wcpower = []
|
wcpower = []
|
||||||
|
getrecords = len(agerecords) == 0
|
||||||
for record in agerecords:
|
for record in agerecords:
|
||||||
wcdurations.append(record.duration)
|
if record.power > 0:
|
||||||
wcpower.append(record.power)
|
wcdurations.append(record.duration)
|
||||||
|
wcpower.append(record.power)
|
||||||
|
else:
|
||||||
|
getrecords = True
|
||||||
|
|
||||||
if len(agerecords)==0:
|
if getrecords:
|
||||||
durations = [1,4,10,20,30,60]
|
durations = [1,4,30,60]
|
||||||
distances = []
|
distances = [100,500,1000,2000,5000,6000,10000,21097,42195]
|
||||||
df2 = pd.DataFrame(
|
df2 = pd.DataFrame(
|
||||||
list(
|
list(
|
||||||
C2WorldClassAgePerformance.objects.filter(
|
C2WorldClassAgePerformance.objects.filter(
|
||||||
@@ -1066,12 +1124,13 @@ def fitscore(rower,workout):
|
|||||||
fitfunc = lambda pars,x: pars[0]/(1+(x/pars[2])) + pars[1]/(1+(x/pars[3]))
|
fitfunc = lambda pars,x: pars[0]/(1+(x/pars[2])) + pars[1]/(1+(x/pars[3]))
|
||||||
errfunc = lambda pars,x,y: fitfunc(pars,x)-y
|
errfunc = lambda pars,x,y: fitfunc(pars,x)-y
|
||||||
|
|
||||||
if len(wcdurations)>4:
|
if len(wcdurations)>=4:
|
||||||
p1wc, success = optimize.leastsq(errfunc, p0[:],args=(wcdurations,wcpower))
|
p1wc, success = optimize.leastsq(errfunc, p0[:],args=(wcdurations,wcpower))
|
||||||
else:
|
else:
|
||||||
factor = fitfunc(p0,wcdurations.mean()/wcpower.mean())
|
factor = fitfunc(p0,wcdurations.mean()/wcpower.mean())
|
||||||
p1wc = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]]
|
p1wc = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]]
|
||||||
success = 0
|
success = 0
|
||||||
|
return 0,0
|
||||||
|
|
||||||
|
|
||||||
times = df['delta']
|
times = df['delta']
|
||||||
@@ -1079,11 +1138,12 @@ def fitscore(rower,workout):
|
|||||||
wcpowers = fitfunc(p1wc,times)
|
wcpowers = fitfunc(p1wc,times)
|
||||||
scores = 100.*powers/wcpowers
|
scores = 100.*powers/wcpowers
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
indexmax = scores.idxmax()
|
indexmax = scores.idxmax()
|
||||||
delta = df.loc[indexmax,'delta']
|
delta = int(df.loc[indexmax,'delta'])
|
||||||
maxvalue = scores.max()
|
maxvalue = scores.max()
|
||||||
except ValueError:
|
except (ValueError,TypeError):
|
||||||
indexmax = 0
|
indexmax = 0
|
||||||
delta = 0
|
delta = 0
|
||||||
maxvalue = 0
|
maxvalue = 0
|
||||||
@@ -1127,7 +1187,6 @@ def setcp(workout,background=False):
|
|||||||
return job.id
|
return job.id
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if not strokesdf.empty:
|
if not strokesdf.empty:
|
||||||
totaltime = strokesdf['time'].max()
|
totaltime = strokesdf['time'].max()
|
||||||
try:
|
try:
|
||||||
@@ -1150,6 +1209,10 @@ def setcp(workout,background=False):
|
|||||||
'id':workout.id,
|
'id':workout.id,
|
||||||
})
|
})
|
||||||
df.to_parquet(filename,engine='fastparquet',compression='GZIP')
|
df.to_parquet(filename,engine='fastparquet',compression='GZIP')
|
||||||
|
goldmedalstandard, goldmedalduration = calculate_goldmedalstandard(workout.user,workout)
|
||||||
|
workout.goldmedalstandard = goldmedalstandard
|
||||||
|
workout.goldmedalduration = goldmedalduration
|
||||||
|
workout.save()
|
||||||
return df,delta,cpvalues
|
return df,delta,cpvalues
|
||||||
|
|
||||||
return pd.DataFrame({'delta':[],'cp':[]}),pd.Series(),pd.Series()
|
return pd.DataFrame({'delta':[],'cp':[]}),pd.Series(),pd.Series()
|
||||||
@@ -2580,7 +2643,10 @@ def read_df_sql(id):
|
|||||||
rowdata,row = getrowdata(id=id)
|
rowdata,row = getrowdata(id=id)
|
||||||
if rowdata and len(rowdata.df):
|
if rowdata and len(rowdata.df):
|
||||||
data = dataprep(rowdata.df,id=id,bands=True,otwpower=True,barchart=True)
|
data = dataprep(rowdata.df,id=id,bands=True,otwpower=True,barchart=True)
|
||||||
df = pd.read_parquet(f)
|
try:
|
||||||
|
df = pd.read_parquet(f)
|
||||||
|
except OSError:
|
||||||
|
df = data
|
||||||
else:
|
else:
|
||||||
df = pd.DataFrame()
|
df = pd.DataFrame()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -768,7 +768,7 @@ class FitnessFitForm(forms.Form):
|
|||||||
fitnesstest = forms.IntegerField(required=True,initial=20,
|
fitnesstest = forms.IntegerField(required=True,initial=20,
|
||||||
label='Test Duration (minutes)')
|
label='Test Duration (minutes)')
|
||||||
|
|
||||||
usefitscore = forms.BooleanField(required=False,initial=False,
|
usegoldmedalstandard = forms.BooleanField(required=False,initial=False,
|
||||||
label='Use best performance against world class')
|
label='Use best performance against world class')
|
||||||
|
|
||||||
kfitness = forms.IntegerField(initial=42,required=True,
|
kfitness = forms.IntegerField(initial=42,required=True,
|
||||||
|
|||||||
+42
-22
@@ -25,7 +25,7 @@ import itertools
|
|||||||
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
|
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
|
||||||
from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation, Band
|
from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation, Band
|
||||||
|
|
||||||
from rowers.utils import myqueue
|
from rowers.utils import myqueue, totaltime_sec_to_string
|
||||||
import django_rq
|
import django_rq
|
||||||
queue = django_rq.get_queue('default')
|
queue = django_rq.get_queue('default')
|
||||||
queuelow = django_rq.get_queue('low')
|
queuelow = django_rq.get_queue('low')
|
||||||
@@ -102,39 +102,52 @@ import rowers.datautils as datautils
|
|||||||
|
|
||||||
from pandas.core.groupby.groupby import DataError
|
from pandas.core.groupby.groupby import DataError
|
||||||
|
|
||||||
def get_fitscore(workouts,kfitness):
|
def build_goldmedalstandards(workouts,kfitness):
|
||||||
dates = []
|
dates = []
|
||||||
testpower = []
|
testpower = []
|
||||||
|
testduration = []
|
||||||
fatigues = []
|
fatigues = []
|
||||||
fitnesses = []
|
fitnesses = []
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
fitscores = []
|
goldmedalstandards = []
|
||||||
|
goldmedaldurations = []
|
||||||
ids = []
|
ids = []
|
||||||
for w in workouts:
|
for w in workouts:
|
||||||
fitscore,fitnesstestsecs = dataprep.fitscore(w.user,w)
|
goldmedalstandard,goldmedalseconds = dataprep.workout_goldmedalstandard(w)
|
||||||
ids.append(w.id)
|
ids.append(w.id)
|
||||||
fitscores.append(fitscore)
|
goldmedalstandards.append(goldmedalstandard)
|
||||||
|
goldmedaldurations.append(goldmedalseconds)
|
||||||
|
|
||||||
df = pd.DataFrame({'workout':ids,'fitscore':fitscores})
|
df = pd.DataFrame({
|
||||||
|
'workout':ids,
|
||||||
|
'goldmedalstandard':goldmedalstandards,
|
||||||
|
'goldmedalduration':goldmedaldurations,
|
||||||
|
})
|
||||||
|
|
||||||
for w in workouts:
|
for w in workouts:
|
||||||
ids = [w.id for w in workouts.filter(date__gte=w.date-datetime.timedelta(days=kfitness),
|
ids = [w.id for w in workouts.filter(date__gte=w.date-datetime.timedelta(days=kfitness),
|
||||||
date__lte=w.date)]
|
date__lte=w.date)]
|
||||||
|
|
||||||
powerdf = df[df['workout'].isin(ids)]
|
powerdf = df[df['workout'].isin(ids)]
|
||||||
powertest = powerdf['fitscore'].max()
|
indexmax = powerdf['goldmedalstandard'].idxmax()
|
||||||
|
powertest = powerdf['goldmedalstandard'].max()
|
||||||
|
durationtest = powerdf.loc[indexmax,'goldmedalduration']
|
||||||
|
|
||||||
dates.append(datetime.datetime.combine(w.date,datetime.datetime.min.time()))
|
dates.append(datetime.datetime.combine(w.date,datetime.datetime.min.time()))
|
||||||
testpower.append(powertest)
|
testpower.append(powertest)
|
||||||
|
testduration.append(durationtest)
|
||||||
|
|
||||||
fatigues.append(np.nan)
|
fatigues.append(np.nan)
|
||||||
fitnesses.append(np.nan)
|
fitnesses.append(np.nan)
|
||||||
|
|
||||||
return dates, testpower, fatigues, fitnesses
|
return dates, testpower, testduration, fatigues, fitnesses
|
||||||
|
|
||||||
|
|
||||||
def get_testpower(workouts,fitnesstestsecs,kfitness):
|
def get_testpower(workouts,fitnesstestsecs,kfitness):
|
||||||
dates = []
|
dates = []
|
||||||
testpower = []
|
testpower = []
|
||||||
|
testduration = []
|
||||||
fatigues = []
|
fatigues = []
|
||||||
fitnesses = []
|
fitnesses = []
|
||||||
data = []
|
data = []
|
||||||
@@ -192,10 +205,11 @@ def get_testpower(workouts,fitnesstestsecs,kfitness):
|
|||||||
|
|
||||||
dates.append(datetime.datetime.combine(w.date,datetime.datetime.min.time()))
|
dates.append(datetime.datetime.combine(w.date,datetime.datetime.min.time()))
|
||||||
testpower.append(powertest)
|
testpower.append(powertest)
|
||||||
|
testduration.append(fitnesstestsecs)
|
||||||
fatigues.append(np.nan)
|
fatigues.append(np.nan)
|
||||||
fitnesses.append(np.nan)
|
fitnesses.append(np.nan)
|
||||||
|
|
||||||
return dates,testpower,fatigues,fitnesses
|
return dates,testpower, testduration,fatigues,fitnesses
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1632,7 +1646,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'):
|
|||||||
return [script,div,js_resources,css_resources]
|
return [script,div,js_resources,css_resources]
|
||||||
|
|
||||||
def getfatigues(
|
def getfatigues(
|
||||||
fatigues,fitnesses,dates,testpower,
|
fatigues,fitnesses,dates,testpower,testduration,
|
||||||
startdate,enddate,user,metricchoice,kfatigue,kfitness):
|
startdate,enddate,user,metricchoice,kfatigue,kfitness):
|
||||||
|
|
||||||
fatigue = 0
|
fatigue = 0
|
||||||
@@ -1685,8 +1699,9 @@ def getfatigues(
|
|||||||
fitnesses.append(fitness)
|
fitnesses.append(fitness)
|
||||||
dates.append(datetime.datetime.combine(date,datetime.datetime.min.time()))
|
dates.append(datetime.datetime.combine(date,datetime.datetime.min.time()))
|
||||||
testpower.append(np.nan)
|
testpower.append(np.nan)
|
||||||
|
testduration.append(np.nan)
|
||||||
|
|
||||||
return fatigues,fitnesses,dates,testpower,impulses
|
return fatigues,fitnesses,dates,testpower,testduration,impulses
|
||||||
|
|
||||||
def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7,
|
def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7,
|
||||||
metricchoice='trimp',doform=False,dofatigue=False):
|
metricchoice='trimp',doform=False,dofatigue=False):
|
||||||
@@ -1699,6 +1714,7 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7,
|
|||||||
fitnesses = []
|
fitnesses = []
|
||||||
dates = []
|
dates = []
|
||||||
testpower = []
|
testpower = []
|
||||||
|
testduration = []
|
||||||
|
|
||||||
modelchoice = 'coggan'
|
modelchoice = 'coggan'
|
||||||
p0 = 0
|
p0 = 0
|
||||||
@@ -1707,11 +1723,10 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fatigues,fitnesses,dates,testpower,testduration,impulses = getfatigues(fatigues,
|
||||||
fatigues,fitnesses,dates,testpower,impulses = getfatigues(fatigues,
|
|
||||||
fitnesses,
|
fitnesses,
|
||||||
dates,
|
dates,
|
||||||
testpower,
|
testpower,testduration,
|
||||||
startdate,enddate,
|
startdate,enddate,
|
||||||
user,metricchoice,
|
user,metricchoice,
|
||||||
kfatigue,kfitness)
|
kfatigue,kfitness)
|
||||||
@@ -1918,7 +1933,7 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None,
|
|||||||
metricchoice='rscore',
|
metricchoice='rscore',
|
||||||
k1=1,k2=1,p0=100,
|
k1=1,k2=1,p0=100,
|
||||||
modelchoice='tsb',
|
modelchoice='tsb',
|
||||||
usefitscore=False):
|
usegoldmedalstandard=False):
|
||||||
|
|
||||||
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
||||||
|
|
||||||
@@ -1929,12 +1944,12 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None,
|
|||||||
fitnesstestsecs = fitnesstest*60
|
fitnesstestsecs = fitnesstest*60
|
||||||
df = pd.DataFrame()
|
df = pd.DataFrame()
|
||||||
|
|
||||||
if not usefitscore:
|
if not usegoldmedalstandard:
|
||||||
dates,testpower,fatigues,fitnesses = get_testpower(
|
dates,testpower,testduration, fatigues,fitnesses = get_testpower(
|
||||||
workouts,fitnesstestsecs,kfitness
|
workouts,fitnesstestsecs,kfitness
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
dates,testpower,fatigues,fitnesses = get_fitscore(
|
dates,testpower, testduration,fatigues,fitnesses = build_goldmedalstandards(
|
||||||
workouts,kfitness
|
workouts,kfitness
|
||||||
)
|
)
|
||||||
# create CP data
|
# create CP data
|
||||||
@@ -1942,6 +1957,7 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None,
|
|||||||
df = pd.DataFrame({
|
df = pd.DataFrame({
|
||||||
'date':dates,
|
'date':dates,
|
||||||
'testpower':testpower,
|
'testpower':testpower,
|
||||||
|
'testduration':testduration,
|
||||||
'fatigue':fatigues,
|
'fatigue':fatigues,
|
||||||
'fitness':fitnesses,
|
'fitness':fitnesses,
|
||||||
})
|
})
|
||||||
@@ -1962,9 +1978,10 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None,
|
|||||||
testpower = df['testpower'].values.tolist()
|
testpower = df['testpower'].values.tolist()
|
||||||
fatigues = df['fatigue'].values.tolist()
|
fatigues = df['fatigue'].values.tolist()
|
||||||
fitnesses = df['fitness'].values.tolist()
|
fitnesses = df['fitness'].values.tolist()
|
||||||
|
testduration = df['testduration'].values.tolist()
|
||||||
|
|
||||||
fatigues,fitnesses,dates,testpower,impulses = getfatigues(
|
fatigues,fitnesses,dates,testpower,testduration,impulses = getfatigues(
|
||||||
fatigues,fitnesses,dates,testpower,
|
fatigues,fitnesses,dates,testpower,testduration,
|
||||||
startdate,enddate,user,metricchoice,kfatigue,kfitness
|
startdate,enddate,user,metricchoice,kfatigue,kfitness
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1972,6 +1989,7 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None,
|
|||||||
df = pd.DataFrame({
|
df = pd.DataFrame({
|
||||||
'date':dates,
|
'date':dates,
|
||||||
'testpower':testpower,
|
'testpower':testpower,
|
||||||
|
'testduration':testduration,
|
||||||
'fatigue':fatigues,
|
'fatigue':fatigues,
|
||||||
'fitness':fitnesses,
|
'fitness':fitnesses,
|
||||||
})
|
})
|
||||||
@@ -1997,6 +2015,7 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None,
|
|||||||
source = ColumnDataSource(
|
source = ColumnDataSource(
|
||||||
data = dict(
|
data = dict(
|
||||||
testpower = df['testpower'],
|
testpower = df['testpower'],
|
||||||
|
testduration = df['testduration'].apply(lambda x:totaltime_sec_to_string(x,shorten=True)),
|
||||||
date = df['date'],
|
date = df['date'],
|
||||||
fdate = df['date'].map(lambda x: x.strftime('%d-%m-%Y')),
|
fdate = df['date'].map(lambda x: x.strftime('%d-%m-%Y')),
|
||||||
fitness = df['fitness'],
|
fitness = df['fitness'],
|
||||||
@@ -2051,7 +2070,7 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None,
|
|||||||
formlabel = 'TSB'
|
formlabel = 'TSB'
|
||||||
rightaxlabel = 'Coggan CTL/ATL/TSB'
|
rightaxlabel = 'Coggan CTL/ATL/TSB'
|
||||||
|
|
||||||
if usefitscore:
|
if usegoldmedalstandard:
|
||||||
legend_label = 'Test Score'
|
legend_label = 'Test Score'
|
||||||
yaxlabel = 'Test Score'
|
yaxlabel = 'Test Score'
|
||||||
else:
|
else:
|
||||||
@@ -2106,7 +2125,8 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None,
|
|||||||
hover = plot.select(dict(type=HoverTool))
|
hover = plot.select(dict(type=HoverTool))
|
||||||
|
|
||||||
hover.tooltips = OrderedDict([
|
hover.tooltips = OrderedDict([
|
||||||
(legend_label,'@testpower'),
|
(legend_label,'@testpower{int}'),
|
||||||
|
('Test', '@testduration'),
|
||||||
('Date','@fdate'),
|
('Date','@fdate'),
|
||||||
(fitlabel,'@fitness'),
|
(fitlabel,'@fitness'),
|
||||||
(fatiguelabel,'@fatigue'),
|
(fatiguelabel,'@fatigue'),
|
||||||
|
|||||||
@@ -2953,6 +2953,7 @@ class Workout(models.Model):
|
|||||||
normv = models.FloatField(default=-1,blank=True)
|
normv = models.FloatField(default=-1,blank=True)
|
||||||
normw = models.FloatField(default=-1,blank=True)
|
normw = models.FloatField(default=-1,blank=True)
|
||||||
goldmedalstandard = models.FloatField(default=-1,blank=True,verbose_name='Gold Medal Standard')
|
goldmedalstandard = models.FloatField(default=-1,blank=True,verbose_name='Gold Medal Standard')
|
||||||
|
goldmedalseconds = models.IntegerField(default=0,blank=True,verbose_name='Gold Medal Seconds')
|
||||||
rpe = models.IntegerField(default=0,blank=True,choices=rpechoices,
|
rpe = models.IntegerField(default=0,blank=True,choices=rpechoices,
|
||||||
verbose_name='Rate of Perceived Exertion')
|
verbose_name='Rate of Perceived Exertion')
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -16,6 +16,7 @@ import json
|
|||||||
|
|
||||||
from scipy import optimize
|
from scipy import optimize
|
||||||
from scipy.signal import savgol_filter
|
from scipy.signal import savgol_filter
|
||||||
|
from scipy.interpolate import griddata
|
||||||
|
|
||||||
import rowingdata
|
import rowingdata
|
||||||
from rowingdata import make_cumvalues
|
from rowingdata import make_cumvalues
|
||||||
@@ -333,7 +334,11 @@ def getagegrouprecord(age,sex='male',weightcategory='hwt',
|
|||||||
|
|
||||||
power = 0.5*(np.abs(power)+power)
|
power = 0.5*(np.abs(power)+power)
|
||||||
else:
|
else:
|
||||||
power = 0
|
new_age = np.range([age])
|
||||||
|
ww = griddata(ages.values,
|
||||||
|
powers.values,
|
||||||
|
new_age,method='linear',rescale=True)
|
||||||
|
power = 0.5*(np.abs(power)+power)
|
||||||
else:
|
else:
|
||||||
power = 0
|
power = 0
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,13 @@
|
|||||||
</table>
|
</table>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
|
<p>Gold Medal Standard: For rowing workouts, the best performance, relative to world class rowers
|
||||||
|
of your age, gender and weight category,
|
||||||
|
found in this workout. This metric uses your power data over time and
|
||||||
|
compares them with the power that the best rowers of your age, gender and weight category
|
||||||
|
can hold over time.</p>
|
||||||
|
<p>Gold Medal Standard Duration: The time interval over which your best
|
||||||
|
performance in this workout was achieved.</p>
|
||||||
<p>rPower: Equivalent steady state power for the duration of the workout.</p>
|
<p>rPower: Equivalent steady state power for the duration of the workout.</p>
|
||||||
<p>Heart Rate Drift: Comparing heart rate normalized for average power for the first and second half of the workout</p>
|
<p>Heart Rate Drift: Comparing heart rate normalized for average power for the first and second half of the workout</p>
|
||||||
<p>TRIMP: TRaining IMPact. A way to combine duration and heart rate into a single number.</p>
|
<p>TRIMP: TRaining IMPact. A way to combine duration and heart rate into a single number.</p>
|
||||||
|
|||||||
+30
-8
@@ -327,7 +327,10 @@ def calculate_age(born,today=None):
|
|||||||
if not today:
|
if not today:
|
||||||
today = date.today()
|
today = date.today()
|
||||||
if born:
|
if born:
|
||||||
return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
|
try:
|
||||||
|
return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
|
||||||
|
except AttributeError:
|
||||||
|
return None
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -373,7 +376,9 @@ def wavg(group, avg_name, weight_name):
|
|||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
return d.mean()
|
return d.mean()
|
||||||
|
|
||||||
def totaltime_sec_to_string(totaltime):
|
def totaltime_sec_to_string(totaltime,shorten=False):
|
||||||
|
if np.isnan(totaltime):
|
||||||
|
return ''
|
||||||
hours = int(totaltime / 3600.)
|
hours = int(totaltime / 3600.)
|
||||||
if hours > 23:
|
if hours > 23:
|
||||||
message = 'Warning: The workout duration was longer than 23 hours. '
|
message = 'Warning: The workout duration was longer than 23 hours. '
|
||||||
@@ -397,12 +402,29 @@ def totaltime_sec_to_string(totaltime):
|
|||||||
if not message:
|
if not message:
|
||||||
message = 'Warning: there is something wrong with the workout duration'
|
message = 'Warning: there is something wrong with the workout duration'
|
||||||
|
|
||||||
duration = "{hours:02d}:{minutes:02d}:{seconds:02d}.{tenths}".format(
|
duration = ""
|
||||||
hours=hours,
|
if not shorten:
|
||||||
minutes=minutes,
|
duration = "{hours:02d}:{minutes:02d}:{seconds:02d}.{tenths}".format(
|
||||||
seconds=seconds,
|
hours=hours,
|
||||||
tenths=tenths
|
minutes=minutes,
|
||||||
)
|
seconds=seconds,
|
||||||
|
tenths=tenths
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if hours != 0:
|
||||||
|
duration = "{hours}:{minutes:02d}:{seconds:02d}".format(
|
||||||
|
hours=hours,
|
||||||
|
minutes=minutes,
|
||||||
|
seconds=seconds,
|
||||||
|
tenths=tenths
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
duration = "{minutes}:{seconds:02d}".format(
|
||||||
|
hours=hours,
|
||||||
|
minutes=minutes,
|
||||||
|
seconds=seconds,
|
||||||
|
tenths=tenths
|
||||||
|
)
|
||||||
|
|
||||||
return duration
|
return duration
|
||||||
|
|
||||||
|
|||||||
@@ -1561,7 +1561,7 @@ def performancemanager_view(request,userid=0,mode='rower',
|
|||||||
fitnesstest = 20
|
fitnesstest = 20
|
||||||
metricchoice = 'trimp'
|
metricchoice = 'trimp'
|
||||||
modelchoice = 'tsb'
|
modelchoice = 'tsb'
|
||||||
usefitscore = False
|
usegoldmedalstandard = False
|
||||||
doform = therower.showfresh
|
doform = therower.showfresh
|
||||||
dofatigue = therower.showfit
|
dofatigue = therower.showfit
|
||||||
|
|
||||||
@@ -1647,7 +1647,7 @@ def fitness_from_cp_view(request,userid=0,mode='rower',
|
|||||||
fitnesstest = 20
|
fitnesstest = 20
|
||||||
metricchoice = 'trimp'
|
metricchoice = 'trimp'
|
||||||
modelchoice = 'tsb'
|
modelchoice = 'tsb'
|
||||||
usefitscore = False
|
usegoldmedalstandard = False
|
||||||
|
|
||||||
# temp fit parameters
|
# temp fit parameters
|
||||||
k1 = 1
|
k1 = 1
|
||||||
@@ -1669,7 +1669,7 @@ def fitness_from_cp_view(request,userid=0,mode='rower',
|
|||||||
k2 = form.cleaned_data['k2']
|
k2 = form.cleaned_data['k2']
|
||||||
p0 = form.cleaned_data['p0']
|
p0 = form.cleaned_data['p0']
|
||||||
modelchoice = form.cleaned_data['modelchoice']
|
modelchoice = form.cleaned_data['modelchoice']
|
||||||
usefitscore = form.cleaned_data['usefitscore']
|
usegoldmedalstandard = form.cleaned_data['usegoldmedalstandard']
|
||||||
else:
|
else:
|
||||||
form = FitnessFitForm()
|
form = FitnessFitForm()
|
||||||
|
|
||||||
@@ -1694,7 +1694,7 @@ def fitness_from_cp_view(request,userid=0,mode='rower',
|
|||||||
metricchoice=metricchoice,
|
metricchoice=metricchoice,
|
||||||
k1=k1,k2=k2,p0=p0,
|
k1=k1,k2=k2,p0=p0,
|
||||||
modelchoice=modelchoice,
|
modelchoice=modelchoice,
|
||||||
usefitscore=usefitscore,
|
usegoldmedalstandard=usegoldmedalstandard,
|
||||||
)
|
)
|
||||||
|
|
||||||
breadcrumbs = [
|
breadcrumbs = [
|
||||||
|
|||||||
@@ -554,7 +554,10 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False):
|
|||||||
if rowerid != 0:
|
if rowerid != 0:
|
||||||
r = Rower.objects.get(id=rowerid)
|
r = Rower.objects.get(id=rowerid)
|
||||||
elif userid != 0:
|
elif userid != 0:
|
||||||
u = User.objects.get(id=userid)
|
try:
|
||||||
|
u = User.objects.get(id=userid)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
raise Http404("User does not exist")
|
||||||
r = getrower(u)
|
r = getrower(u)
|
||||||
else:
|
else:
|
||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import rowers.mytypes as mytypes
|
|||||||
import numpy
|
import numpy
|
||||||
from rowers.mailprocessing import send_confirm
|
from rowers.mailprocessing import send_confirm
|
||||||
import rowers.uploads as uploads
|
import rowers.uploads as uploads
|
||||||
|
import rowers.utils as utils
|
||||||
|
|
||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
from json.decoder import JSONDecodeError
|
from json.decoder import JSONDecodeError
|
||||||
@@ -3497,6 +3498,22 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
|
|||||||
|
|
||||||
# Normalized power & TSS
|
# Normalized power & TSS
|
||||||
tss,normp = dataprep.workout_rscore(w)
|
tss,normp = dataprep.workout_rscore(w)
|
||||||
|
goldmedalstandard,goldmedalseconds = dataprep.workout_goldmedalstandard(w)
|
||||||
|
|
||||||
|
|
||||||
|
#if not np.isnan(goldmedalstandard) and goldmedalstandard > 0:
|
||||||
|
# otherstats['goldmedalstandard'] = {
|
||||||
|
# 'verbose_name': 'Gold Medal Standard',
|
||||||
|
# 'value': int(goldmedalstandard),
|
||||||
|
# 'unit': '%',
|
||||||
|
# }
|
||||||
|
|
||||||
|
#if not np.isnan(goldmedalseconds) and goldmedalseconds > 0:
|
||||||
|
# otherstats['goldmedalseconds'] = {
|
||||||
|
# 'verbose_name': 'Gold Medal Standard Duration',
|
||||||
|
# 'value': utils.totaltime_sec_to_string(goldmedalseconds,shorten=True),
|
||||||
|
# 'unit': '',
|
||||||
|
# }
|
||||||
|
|
||||||
|
|
||||||
if not np.isnan(tss) and tss != 0:
|
if not np.isnan(tss) and tss != 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user