adding cpoverlay (world class)
This commit is contained in:
@@ -1218,6 +1218,10 @@ class AnalysisChoiceForm(forms.Form):
|
|||||||
cpfit = forms.ChoiceField(choices=cpfitchoices,
|
cpfit = forms.ChoiceField(choices=cpfitchoices,
|
||||||
label = 'Model Fit',initial='data',required=False)
|
label = 'Model Fit',initial='data',required=False)
|
||||||
|
|
||||||
|
cpoverlay = forms.BooleanField(initial=False,
|
||||||
|
label='Overlay World Record Performance',
|
||||||
|
required=False)
|
||||||
|
|
||||||
piece = forms.IntegerField(initial=4,label='Ranking Piece (minutes)',
|
piece = forms.IntegerField(initial=4,label='Ranking Piece (minutes)',
|
||||||
required=False)
|
required=False)
|
||||||
|
|
||||||
|
|||||||
@@ -3304,7 +3304,8 @@ def interactive_agegroupcpchart(age,normalized=False):
|
|||||||
|
|
||||||
|
|
||||||
def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data',
|
def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data',
|
||||||
title='',type='water'):
|
title='',type='water',
|
||||||
|
wcpower=[],wcdurations=[],cpoverlay=False):
|
||||||
|
|
||||||
powerdf = powerdf[~(powerdf == 0).any(axis=1)]
|
powerdf = powerdf[~(powerdf == 0).any(axis=1)]
|
||||||
# plot tools
|
# plot tools
|
||||||
@@ -3356,6 +3357,34 @@ def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data',
|
|||||||
workouts = powerdf['workout']
|
workouts = powerdf['workout']
|
||||||
urls = powerdf['url']
|
urls = powerdf['url']
|
||||||
|
|
||||||
|
# add world class
|
||||||
|
wcpower = pd.Series(wcpower)
|
||||||
|
wcdurations = pd.Series(wcdurations)
|
||||||
|
|
||||||
|
|
||||||
|
# fitting WC data to three parameter CP model
|
||||||
|
if len(wcdurations)>=4:
|
||||||
|
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
|
||||||
|
p1wc, success = optimize.leastsq(errfunc, p0[:],
|
||||||
|
args = (wcdurations,wcpower))
|
||||||
|
else:
|
||||||
|
p1wc = None
|
||||||
|
|
||||||
|
if p1wc is not None and cpoverlay:
|
||||||
|
fitpowerwc = fitfunc(p1wc,fitt)
|
||||||
|
fitpowerexcellent = 0.7*fitfunc(p1wc,fitt)
|
||||||
|
fitpowergood = 0.6*fitfunc(p1wc,fitt)
|
||||||
|
fitpowerfair = 0.5*fitfunc(p1wc,fitt)
|
||||||
|
fitpoweraverage = 0.4*fitfunc(p1wc,fitt)
|
||||||
|
|
||||||
|
else:
|
||||||
|
fitpowerwc = 0*fitpower
|
||||||
|
fitpowerexcellent = 0*fitpower
|
||||||
|
fitpowergood = 0*fitpower
|
||||||
|
fitpowerfair = 0*fitpower
|
||||||
|
fitpoweraverage = 0*fitpower
|
||||||
|
|
||||||
|
|
||||||
sourcecomplex = ColumnDataSource(
|
sourcecomplex = ColumnDataSource(
|
||||||
data = dict(
|
data = dict(
|
||||||
@@ -3364,6 +3393,11 @@ def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data',
|
|||||||
duration = fitt/60.,
|
duration = fitt/60.,
|
||||||
ftime = ftime,
|
ftime = ftime,
|
||||||
workout = workouts,
|
workout = workouts,
|
||||||
|
fitpowerwc = fitpowerwc,
|
||||||
|
fitpowerexcellent = fitpowerexcellent,
|
||||||
|
fitpowergood = fitpowergood,
|
||||||
|
fitpowerfair = fitpowerfair,
|
||||||
|
fitpoweraverage = fitpoweraverage,
|
||||||
url = urls,
|
url = urls,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -3424,6 +3458,7 @@ def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data',
|
|||||||
('Power (W)','@CP{int}'),
|
('Power (W)','@CP{int}'),
|
||||||
('Power (W) upper','@CPmax{int}'),
|
('Power (W) upper','@CPmax{int}'),
|
||||||
('Workout','@workout'),
|
('Workout','@workout'),
|
||||||
|
('World Class','@fitpowerwc{int}')
|
||||||
])
|
])
|
||||||
|
|
||||||
hover.mode = 'mouse'
|
hover.mode = 'mouse'
|
||||||
@@ -3437,6 +3472,27 @@ def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data',
|
|||||||
plot.line('duration','CPmax',source=sourcecomplex,legend_label="CP Model",
|
plot.line('duration','CPmax',source=sourcecomplex,legend_label="CP Model",
|
||||||
color='red')
|
color='red')
|
||||||
|
|
||||||
|
if p1wc is not None:
|
||||||
|
plot.line('duration','fitpowerwc',source=sourcecomplex,
|
||||||
|
legend_label="World Class",
|
||||||
|
color='darkgoldenrod',line_dash='dotted')
|
||||||
|
|
||||||
|
plot.line('duration','fitpowerexcellent',source=sourcecomplex,
|
||||||
|
legend_label="90% percentile",
|
||||||
|
color='goldenrod',line_dash='dotted')
|
||||||
|
|
||||||
|
plot.line('duration','fitpowergood',source=sourcecomplex,
|
||||||
|
legend_label="75% percentile",
|
||||||
|
color='sandybrown',line_dash='dotted')
|
||||||
|
|
||||||
|
plot.line('duration','fitpowerfair',source=sourcecomplex,
|
||||||
|
legend_label="50% percentile",
|
||||||
|
color='rosybrown',line_dash='dotted')
|
||||||
|
|
||||||
|
plot.line('duration','fitpoweraverage',source=sourcecomplex,
|
||||||
|
legend_label="25% percentile",
|
||||||
|
color='tan',line_dash='dotted')
|
||||||
|
|
||||||
script, div = components(plot)
|
script, div = components(plot)
|
||||||
|
|
||||||
return [script,div,p1,ratio,message]
|
return [script,div,p1,ratio,message]
|
||||||
|
|||||||
@@ -91,6 +91,7 @@
|
|||||||
var reststrokes = $("#id_includereststrokes").parent().parent();
|
var reststrokes = $("#id_includereststrokes").parent().parent();
|
||||||
var piece = $("#id_piece").parent().parent();
|
var piece = $("#id_piece").parent().parent();
|
||||||
var cpfit = $("#id_cpfit").parent().parent();
|
var cpfit = $("#id_cpfit").parent().parent();
|
||||||
|
var cpoverlay = $("#id_cpoverlay").parent().parent();
|
||||||
|
|
||||||
|
|
||||||
// Hide the fields.
|
// Hide the fields.
|
||||||
@@ -113,6 +114,7 @@
|
|||||||
spmmin.hide();
|
spmmin.hide();
|
||||||
spmmax.hide();
|
spmmax.hide();
|
||||||
cpfit.hide();
|
cpfit.hide();
|
||||||
|
cpoverlay.hide();
|
||||||
piece.hide();
|
piece.hide();
|
||||||
|
|
||||||
if (functionfield.val() == 'boxplot') {
|
if (functionfield.val() == 'boxplot') {
|
||||||
@@ -153,6 +155,7 @@
|
|||||||
if (functionfield.val() == 'cp') {
|
if (functionfield.val() == 'cp') {
|
||||||
cpfit.show();
|
cpfit.show();
|
||||||
piece.show();
|
piece.show();
|
||||||
|
cpoverlay.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -182,6 +185,7 @@
|
|||||||
plottype.hide();
|
plottype.hide();
|
||||||
reststrokes.show();
|
reststrokes.show();
|
||||||
cpfit.hide();
|
cpfit.hide();
|
||||||
|
cpoverlay.hide();
|
||||||
piece.hide();
|
piece.hide();
|
||||||
}
|
}
|
||||||
else if (Value=='histo') {
|
else if (Value=='histo') {
|
||||||
@@ -202,6 +206,7 @@
|
|||||||
plottype.hide();
|
plottype.hide();
|
||||||
reststrokes.show();
|
reststrokes.show();
|
||||||
cpfit.hide();
|
cpfit.hide();
|
||||||
|
cpoverlay.hide();
|
||||||
piece.hide();
|
piece.hide();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -223,6 +228,7 @@
|
|||||||
plottype.hide();
|
plottype.hide();
|
||||||
reststrokes.show();
|
reststrokes.show();
|
||||||
cpfit.hide();
|
cpfit.hide();
|
||||||
|
cpoverlay.hide();
|
||||||
piece.hide();
|
piece.hide();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -244,6 +250,7 @@
|
|||||||
errorbars.hide();
|
errorbars.hide();
|
||||||
reststrokes.show();
|
reststrokes.show();
|
||||||
cpfit.hide();
|
cpfit.hide();
|
||||||
|
cpoverlay.hide();
|
||||||
piece.hide();
|
piece.hide();
|
||||||
}
|
}
|
||||||
else if (Value=='stats') {
|
else if (Value=='stats') {
|
||||||
@@ -260,6 +267,7 @@
|
|||||||
plottype.hide();
|
plottype.hide();
|
||||||
reststrokes.show();
|
reststrokes.show();
|
||||||
cpfit.hide();
|
cpfit.hide();
|
||||||
|
cpoverlay.hide();
|
||||||
piece.hide();
|
piece.hide();
|
||||||
}
|
}
|
||||||
else if (Value=='compare') {
|
else if (Value=='compare') {
|
||||||
@@ -280,6 +288,7 @@
|
|||||||
errorbars.hide();
|
errorbars.hide();
|
||||||
piece.hide();
|
piece.hide();
|
||||||
cpfit.hide();
|
cpfit.hide();
|
||||||
|
cpoverlay.hide();
|
||||||
reststrokes.show();
|
reststrokes.show();
|
||||||
|
|
||||||
|
|
||||||
@@ -302,6 +311,7 @@
|
|||||||
plottype.hide();
|
plottype.hide();
|
||||||
reststrokes.hide();
|
reststrokes.hide();
|
||||||
cpfit.show();
|
cpfit.show();
|
||||||
|
cpoverlay.hide();
|
||||||
piece.show();
|
piece.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -372,6 +382,126 @@
|
|||||||
<input name='optionsform' type="submit" value="Submit">
|
<input name='optionsform' type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
|
{% if worldclass %}
|
||||||
|
{% if age and sex != 'not specified' %}
|
||||||
|
<li class="grid_4">
|
||||||
|
<h2>World Records</h2>
|
||||||
|
<p>The dashed lines are based on the
|
||||||
|
<a href="https://log.concept2.com/rankings">Concept2</a>
|
||||||
|
rankings for your age ({{ age }}), gender ({{ sex }})
|
||||||
|
and weight category ({{ weightcategory }}). World class means within 5% of
|
||||||
|
<a href="http://www.concept2.com/indoor-rowers/racing/records/world">
|
||||||
|
World Record</a> in terms
|
||||||
|
of power.
|
||||||
|
The percentile lines are estimates of where the percentiles
|
||||||
|
of the Concept2 rankings historically are for those of exactly
|
||||||
|
your age, gender and weight class.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For rowing on the water, the results are corrected for the expected
|
||||||
|
difference in power between the Concept2 indoor rower and power
|
||||||
|
values on the water.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<table width="100%" class="listtable">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a target="_"
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/100m/">100m
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a target="_"
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/500m/">500m
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a target="_"
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/1000m/">1000m
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a target="_"
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/2000m/">2000m
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a target="_"
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/5000m/">5000m
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a target="_"
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/6000m/">6000m
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a target="_"
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/10000m/">10000m
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a target="_"
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/21097m/">Half Marathon
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/42195m/">Full Marathon
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/1min/">1 minute
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/4min/">4 minutes
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/30min/">30 minutes
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/60min/">1 hour
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id=''):
|
|||||||
r = getrequestrower(request, userid=userid)
|
r = getrequestrower(request, userid=userid)
|
||||||
user = r.user
|
user = r.user
|
||||||
userid = user.id
|
userid = user.id
|
||||||
|
worldclass = False
|
||||||
|
|
||||||
firstworkout = None
|
firstworkout = None
|
||||||
if id:
|
if id:
|
||||||
@@ -71,6 +72,10 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id=''):
|
|||||||
modalities = [m[0] for m in mytypes.workouttypes_ordered.items()]
|
modalities = [m[0] for m in mytypes.workouttypes_ordered.items()]
|
||||||
modality = 'all'
|
modality = 'all'
|
||||||
|
|
||||||
|
try:
|
||||||
|
worldclass = options['cpoverlay']
|
||||||
|
except KeyError:
|
||||||
|
worldclass = False
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -132,6 +137,12 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id=''):
|
|||||||
|
|
||||||
options['modalities'] = modalities
|
options['modalities'] = modalities
|
||||||
options['waterboattype'] = waterboattype
|
options['waterboattype'] = waterboattype
|
||||||
|
try:
|
||||||
|
worldclass = options['cpoverlay']
|
||||||
|
except KeyError:
|
||||||
|
worldclass = False
|
||||||
|
options['cpoverlay'] = worldclass
|
||||||
|
|
||||||
|
|
||||||
chartform = AnalysisChoiceForm(request.POST)
|
chartform = AnalysisChoiceForm(request.POST)
|
||||||
if chartform.is_valid():
|
if chartform.is_valid():
|
||||||
@@ -240,6 +251,10 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id=''):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if r.birthdate:
|
||||||
|
age = calculate_age(r.birthdate)
|
||||||
|
else:
|
||||||
|
age = 0
|
||||||
|
|
||||||
startdatestring = startdate.strftime('%Y-%m-%d')
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
||||||
enddatestring = enddate.strftime('%Y-%m-%d')
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
||||||
@@ -272,6 +287,10 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id=''):
|
|||||||
'chartform':chartform,
|
'chartform':chartform,
|
||||||
'searchform':searchform,
|
'searchform':searchform,
|
||||||
'optionsform':optionsform,
|
'optionsform':optionsform,
|
||||||
|
'worldclass':worldclass,
|
||||||
|
'age':age,
|
||||||
|
'sex':r.sex,
|
||||||
|
'weightcategory':r.weightcategory,
|
||||||
'teams':get_my_teams(request.user),
|
'teams':get_my_teams(request.user),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -519,6 +538,7 @@ def histodata(workouts, options):
|
|||||||
def cpdata(workouts, options):
|
def cpdata(workouts, options):
|
||||||
userid = options['userid']
|
userid = options['userid']
|
||||||
cpfit = options['cpfit']
|
cpfit = options['cpfit']
|
||||||
|
cpoverlay = options['cpoverlay']
|
||||||
|
|
||||||
u = User.objects.get(id=userid)
|
u = User.objects.get(id=userid)
|
||||||
r = u.rower
|
r = u.rower
|
||||||
@@ -546,6 +566,9 @@ def cpdata(workouts, options):
|
|||||||
|
|
||||||
rowername = r.user.first_name+" "+r.user.last_name
|
rowername = r.user.first_name+" "+r.user.last_name
|
||||||
|
|
||||||
|
wcdurations = []
|
||||||
|
wcpower = []
|
||||||
|
|
||||||
if len(powerdf) !=0 :
|
if len(powerdf) !=0 :
|
||||||
datefirst = pd.Series(w.date for w in workouts).min()
|
datefirst = pd.Series(w.date for w in workouts).min()
|
||||||
datelast = pd.Series(w.date for w in workouts).max()
|
datelast = pd.Series(w.date for w in workouts).max()
|
||||||
@@ -561,19 +584,42 @@ def cpdata(workouts, options):
|
|||||||
# for Mike
|
# for Mike
|
||||||
wtype = 'erg'
|
wtype = 'erg'
|
||||||
|
|
||||||
|
if cpoverlay:
|
||||||
|
if r.birthdate:
|
||||||
|
age = calculate_age(r.birthdate)
|
||||||
|
else:
|
||||||
|
worldclasspower = None
|
||||||
|
age = 0
|
||||||
|
|
||||||
|
agerecords = CalcAgePerformance.objects.filter(
|
||||||
|
age = age,
|
||||||
|
sex = r.sex,
|
||||||
|
weightcategory = r.weightcategory
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(agerecords) == 0:
|
||||||
|
wcpower = []
|
||||||
|
wcdurations = []
|
||||||
|
else:
|
||||||
|
wcdurations = []
|
||||||
|
wcpower = []
|
||||||
|
for record in agerecords:
|
||||||
|
recordpower = record.power
|
||||||
|
if wtype == 'water':
|
||||||
|
recordpower = record.power*(100.-r.otwslack)/100.
|
||||||
|
|
||||||
|
wcdurations.append(record.duration)
|
||||||
|
wcpower.append(recordpower)
|
||||||
|
|
||||||
res = interactive_otwcpchart(powerdf,promember=True,rowername=rowername,r=r,
|
res = interactive_otwcpchart(powerdf,promember=True,rowername=rowername,r=r,
|
||||||
cpfit=cpfit,title=title,type=wtype)
|
cpfit=cpfit,title=title,type=wtype,
|
||||||
|
cpoverlay=cpoverlay,
|
||||||
|
wcdurations=wcdurations,wcpower=wcpower)
|
||||||
|
|
||||||
script = res[0]
|
script = res[0]
|
||||||
div = res[1]
|
div = res[1]
|
||||||
p1 = res[2]
|
p1 = res[2]
|
||||||
ratio = res[3]
|
ratio = res[3]
|
||||||
#r.p0 = p1[0]
|
|
||||||
#r.p1 = p1[1]
|
|
||||||
#r.p2 = p1[2]
|
|
||||||
#r.p3 = p1[3]
|
|
||||||
#r.cpratio = ratio
|
|
||||||
#r.save()
|
|
||||||
paulslope = 1
|
paulslope = 1
|
||||||
paulintercept = 1
|
paulintercept = 1
|
||||||
message = res[4]
|
message = res[4]
|
||||||
@@ -2194,7 +2240,7 @@ def rankings_view2(request,userid=0,
|
|||||||
if len(agerecords) == 0:
|
if len(agerecords) == 0:
|
||||||
recalc = True
|
recalc = True
|
||||||
wcpower = []
|
wcpower = []
|
||||||
wcduration = []
|
wcdurations = []
|
||||||
else:
|
else:
|
||||||
wcdurations = []
|
wcdurations = []
|
||||||
wcpower = []
|
wcpower = []
|
||||||
|
|||||||
Reference in New Issue
Block a user