Private
Public Access
1
0

Merge branch 'feature/c2agerecords' into develop

This commit is contained in:
Sander Roosendaal
2017-12-13 15:44:43 +01:00
11 changed files with 698 additions and 7 deletions

View File

@@ -5,7 +5,7 @@ from django.contrib.auth.models import User
from .models import (
Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement,
Team,TeamInvite,TeamRequest,
WorkoutComment,
WorkoutComment,C2WorldClassAgePerformance,
)
# Register your models here so you can use them in the Admin module
@@ -26,6 +26,9 @@ class WorkoutAdmin(admin.ModelAdmin):
class FavoriteChartAdmin(admin.ModelAdmin):
list_display = ('user','xparam','yparam1','yparam2','plottype','workouttype','reststrokes')
class C2WorldClassAgePerformanceAdmin(admin.ModelAdmin):
list_display = ('sex','weightcategory','age','distance','power','name','season')
class SiteAnnouncementAdmin(admin.ModelAdmin):
list_display = ('announcement','created','modified','expires','dotweet')
@@ -51,3 +54,5 @@ admin.site.register(SiteAnnouncement,SiteAnnouncementAdmin)
admin.site.register(TeamInvite,TeamInviteAdmin)
admin.site.register(TeamRequest,TeamRequestAdmin)
admin.site.register(WorkoutComment,WorkoutCommentAdmin)
admin.site.register(C2WorldClassAgePerformance,
C2WorldClassAgePerformanceAdmin)

View File

@@ -473,6 +473,10 @@ def strfdelta(tdelta):
return res
def timedelta_to_seconds(tdelta):
return 60.*tdelta.minute+tdelta.second
# A nice printable format for pace values

View File

@@ -1069,8 +1069,40 @@ def interactive_otwcpchart(powerdf,promember=0):
return [script,div,p1,ratio,message]
def interactive_agegroup_plot(df):
age = df['age']
power = df['power']
poly_coefficients = np.polyfit(age,power,6)
age2 = np.linspace(11,95)
poly_vals = np.polyval(poly_coefficients,age2)
source = ColumnDataSource(
data = dict(
age = age,
power = power,
age2 = age2,
poly_vals = poly_vals
)
)
plot = Figure(plot_width=900)
plot.circle('age','power',source=source,fill_color='red',size=15,
legend='2k Power')
plot.line(age2,poly_vals)
plot.xaxis.axis_label = "Age"
plot.yaxis.axis_label = "Concept2 2k power"
script,div = components(plot)
return script,div
def interactive_cpchart(rower,thedistances,thesecs,theavpower,
theworkouts,promember=0):
theworkouts,promember=0,
wcpower=[],wcdurations=[]):
message = 0
# plot tools
@@ -1136,11 +1168,23 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower,
)
# fitting the data to three parameter CP model
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
p0 = [500,350,10,8000]
wcpower = pd.Series(wcpower)
wcdurations = pd.Series(wcdurations)
# fitting WC data to three parameter CP model
if len(wcdurations)>=4:
p1wc, success = optimize.leastsq(errfunc, p0[:],
args = (wcdurations,wcpower))
else:
p1wc = None
# fitting the data to three parameter CP model
p1 = p0
if len(thesecs)>=4:
@@ -1153,6 +1197,21 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower,
fitt = pd.Series(10**(4*np.arange(100)/100.))
fitpower = fitfunc(p1,fitt)
if p1wc is not None:
fitpowerwc = 0.95*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
message = ""
if len(fitpower[fitpower<0]) > 0:
@@ -1172,6 +1231,11 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower,
),
spm = 0*fitpower,
power = fitpower,
fitpowerwc = fitpowerwc,
fitpowerexcellent = fitpowerexcellent,
fitpowergood = fitpowergood,
fitpowerfair = fitpowerfair,
fitpoweraverage = fitpoweraverage,
fpace = nicepaceformat(fitp2),
)
)
@@ -1250,6 +1314,25 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower,
plot.line('duration','power',source=sourcepaul,legend="Paul's Law")
plot.line('duration','power',source=sourcecomplex,legend="CP Model",
color='green')
plot.line('duration','fitpowerwc',source=sourcecomplex,
legend="World Class",
color='Maroon',line_dash='dotted')
plot.line('duration','fitpowerexcellent',source=sourcecomplex,
legend="Excellent",
color='Purple',line_dash='dotted')
plot.line('duration','fitpowergood',source=sourcecomplex,
legend="Good",
color='Olive',line_dash='dotted')
plot.line('duration','fitpowerfair',source=sourcecomplex,
legend="Fair",
color='Gray',line_dash='dotted')
plot.line('duration','fitpoweraverage',source=sourcecomplex,
legend="Average",
color='SkyBlue',line_dash='dotted')
script, div = components(plot)

View File

@@ -1,5 +1,7 @@
from utils import lbstoN
import numpy as np
from models import C2WorldClassAgePerformance
import pandas as pd
rowingmetrics = (
('time',{
@@ -316,3 +318,36 @@ def calc_trimp(df,sex,hrmax,hrmin):
trimp = trimpdata.sum()
return trimp
def getagegrouprecord(age,sex='male',weightcategory='hwt',
distance=2000,duration=None):
if not duration:
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
distance=distance,
sex=sex,
weightcategory=weightcategory
).values()
)
)
else:
duration=60*int(duration)
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
duration=duration,
sex=sex,
weightcategory=weightcategory
).values()
)
)
ages = df['age']
powers = df['power']
poly_coefficients = np.polyfit(ages,powers,6)
power = np.polyval(poly_coefficients,age)
return power

View File

@@ -21,6 +21,8 @@ from sqlalchemy import create_engine
import sqlalchemy as sa
from sqlite3 import OperationalError
from django.utils import timezone
import pandas as pd
from dateutil import parser
import datetime
from django.core.exceptions import ValidationError
from rowers.rows import validate_file_extension
@@ -149,6 +151,103 @@ class PowerZonesField(models.TextField):
value = self._get_val_from_obj(obj)
return self.get_deb_prep_value(value)
c2url = 'http://www.concept2.com/indoor-rowers/racing/records/world?machine=1&event=All&gender=All&age=All&weight=All'
def update_records(url=c2url):
try:
dfs = pd.read_html(url,attrs={'class':'views-table'})
df = dfs[0]
df.columns = df.columns.str.strip()
success = 1
except:
df = pd.DataFrame()
if not df.empty:
C2WorldClassAgePerformance.objects.all().delete()
df.Gender = df.Gender.apply(lambda x: 'male' if x=='M' else 'female')
df['Distance'] = df['Event']
df['Duration'] = 0
for nr,row in df.iterrows():
if 'm' in row['Record']:
df.ix[nr,'Distance'] = row['Record'][:-1]
df.ix[nr,'Duration'] = 60*row['Event']
else:
df.ix[nr,'Distance'] = row['Event']
try:
tobj = datetime.datetime.strptime(row['Record'],'%M:%S.%f')
except ValueError:
tobj = datetime.datetime.strptime(row['Record'],'%H:%M:%S.%f')
df.ix[nr,'Duration'] = 3600.*tobj.hour+60.*tobj.minute+tobj.second+tobj.microsecond/1.e6
print row.Duration
for nr,row in df.iterrows():
try:
weightcategory = row.Weight.lower()
except AttributeError:
weightcategory = 'hwt'
sex = row.Gender
name = row.Name
age = int(row.Age)
distance = int(row.Distance)
duration = float(row.Duration)
season = int(row.Season)
velo = distance/duration
power = int(2.8*velo**3)
record = C2WorldClassAgePerformance(
age = age,
weightcategory = weightcategory,
sex=sex,
distance = distance,
duration = duration,
power = power,
season = season,
name = name,
)
try:
record.save()
except:
print record
class C2WorldClassAgePerformance(models.Model):
weightcategories = (
('hwt','heavy-weight'),
('lwt','light-weight'),
)
sexcategories = (
('male','male'),
('female','female'),
)
weightcategory = models.CharField(default="hwt",
max_length=30,
choices=weightcategories)
sex = models.CharField(default="female",
max_length=30,
choices=sexcategories)
age = models.IntegerField(default=19,verbose_name="Age")
distance = models.IntegerField(default=2000)
name = models.CharField(max_length=200,blank=True)
duration = models.FloatField(default=1,blank=True)
season = models.IntegerField(default=2013)
power = models.IntegerField(default=200)
class Meta:
unique_together = ('age','sex','weightcategory','distance')
def __unicode__(self):
return self.sex+' '+self.weightcategory+' '+self.name+':'+str(self.age)+' ('+str(self.season)+')'
# For future Team functionality
class Team(models.Model):
choices = (

View File

@@ -0,0 +1,47 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Rowsandall {% endblock %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, false, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="workouts" class="grid_12 alpha">
<h1>Interactive Plot</h1>
{{ the_div|safe }}
</div>
{% endblock %}

View File

@@ -79,8 +79,13 @@
<div class="grid_12 alpha">
<div class="grid_6 alpha">
<p>&nbsp;</p>
</div>
<div class="grid_2 suffix_4 alpha">
<p>
<a class="button blue small" href="/rowers/ote-bests2">
Ranking Pieces 2.0</a></p>
<p>Analyze your Concept2 ranking pieces over a date range and predict your pace on other pieces.</p>
</div>
</div>
<div class="grid_6 omega">
<div class="grid_2 alpha">

View File

@@ -160,6 +160,15 @@
{{ the_div|safe }}
<p>The dashed lines are based on the Concept2 rankings for your age, gender
and weight category. World class means within 5% of World Record in terms
of power. Excellent, Good, and Fair indicate the power levels of the top
10%, 25% and 50% of the Concept2 rankings. Average is taken
as being in the top 75%, given that the Concept2 rankings probably
represent the more competitive sub-group of all people who erg.
Please note that this is a prediction for people of exactly your age,
and your actual place in the Concept2 Ranking may be different.</p>
</div>
<div id="predictions" class="grid_12 alpha">
@@ -259,6 +268,7 @@
{% endif %}
</div>
</div>
{% endblock %}

View File

@@ -121,6 +121,12 @@ urlpatterns = [
url(r'^400/$', TemplateView.as_view(template_name='400.html'),name='400'),
url(r'^403/$', TemplateView.as_view(template_name='403.html'),name='403'),
url(r'^imports/$', TemplateView.as_view(template_name='imports.html'), name='imports'),
url(r'^agegrouprecords/(?P<sex>\w+.*)/(?P<weightcategory>\w+.*)/(?P<distance>\d+)m$',
views.agegrouprecordview),
url(r'^agegrouprecords/(?P<sex>\w+.*)/(?P<weightcategory>\w+.*)/(?P<duration>\d+)min$',
views.agegrouprecordview),
url(r'^agegrouprecords/(?P<sex>\w+.*)/(?P<weightcategory>\w+.*)$',
views.agegrouprecordview),
url(r'^list-workouts/ranking$',views.workouts_view,{'rankingonly':True}),
url(r'^list-workouts/team/(?P<teamid>\d+)/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
url(r'^list-workouts/team/(?P<teamid>\d+)/$',views.workouts_view),
@@ -159,6 +165,12 @@ urlpatterns = [
url(r'^ote-bests/(?P<deltadays>\d+)$',views.rankings_view),
url(r'^ote-bests/$',views.rankings_view),
url(r'^(?P<theuser>\d+)/ote-bests/$',views.rankings_view),
url(r'^(?P<theuser>\d+)/ote-bests2/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.rankings_view),
url(r'^(?P<theuser>\d+)/ote-bests2/(?P<deltadays>\d+)$',views.rankings_view2),
url(r'^ote-bests2/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.rankings_view2),
url(r'^ote-bests2/(?P<deltadays>\d+)$',views.rankings_view2),
url(r'^ote-bests2/$',views.rankings_view2),
url(r'^(?P<theuser>\d+)/ote-bests2/$',views.rankings_view2),
url(r'^(?P<theuser>\d+)/otw-bests/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.otwrankings_view),
url(r'^(?P<theuser>\d+)/otw-bests/(?P<deltadays>\d+)$',views.otwrankings_view),
url(r'^otw-bests/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.otwrankings_view),

View File

@@ -263,3 +263,8 @@ def myqueue(queue,function,*args,**kwargs):
return job
from datetime import date
def calculate_age(born):
today = date.today()
return today.year - born.year - ((today.month, today.day) < (born.month, born.day))

View File

@@ -710,7 +710,8 @@ def splitstdata(lijst):
from utils import (
geo_distance,serialize_list,deserialize_list,uniqify,
str2bool,range_to_color_hex,absolute,myqueue,get_call
str2bool,range_to_color_hex,absolute,myqueue,get_call,
calculate_age
)
import datautils
@@ -3220,6 +3221,16 @@ def rankings_view(request,theuser=0,
promember=0
if not request.user.is_anonymous():
r = getrower(request.user)
if r.birthdate:
age = calculate_age(r.birthdate)
worldclasspower = int(metrics.getagegrouprecord(
age,
sex=r.sex,
weightcategory=r.weightcategory
))
else:
worldclasspower = None
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
@@ -3432,6 +3443,345 @@ def rankings_view(request,theuser=0,
for rankingduration in rankingdurations:
t = 3600.*rankingduration.hour
t += 60.*rankingduration.minute
t += rankingduration.second
t += rankingduration.microsecond/1.e6
# Paul's model
ratio = paulintercept/paulslope
u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope
d = 500*t*np.log(10.)
d = d/(paulslope*lambertw(u))
d = d.real
velo = d/t
p = 500./velo
pwr = 2.8*(velo**3)
a = {'distance':int(d),
'duration':timedeltaconv(t),
'pace':timedeltaconv(p),
'power':int(pwr)}
predictions.append(a)
# CP model
pwr = p1[0]/(1+t/p1[2])
pwr += p1[1]/(1+t/p1[3])
if pwr <= 0:
pwr = 50.
velo = (pwr/2.8)**(1./3.)
if np.isnan(velo) or velo <=0:
velo = 1.0
d = t*velo
p = 500./velo
a = {'distance':int(d),
'duration':timedeltaconv(t),
'pace':timedeltaconv(p),
'power':int(pwr)}
cpredictions.append(a)
messages.error(request,message)
return render(request, 'rankings.html',
{'rankingworkouts':theworkouts,
'interactiveplot':script,
'the_div':div,
'predictions':predictions,
'cpredictions':cpredictions,
'nrdata':len(thedistances),
'form':form,
'dateform':dateform,
'deltaform':deltaform,
'worldclasspower':worldclasspower,
'id': theuser,
'theuser':uu,
'startdate':startdate,
'enddate':enddate,
'teams':get_my_teams(request.user),
})
# Show ranking distances including predicted paces
@login_required()
def rankings_view2(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
deltadays=-1,
startdatestring="",
enddatestring=""):
if deltadays>0:
startdate = enddate-datetime.timedelta(days=int(deltadays))
if startdatestring != "":
startdate = iso8601.parse_date(startdatestring)
if enddatestring != "":
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
if theuser == 0:
theuser = request.user.id
promember=0
if not request.user.is_anonymous():
r = getrower(request.user)
wcdurations = []
wcpower = []
if r.birthdate:
age = calculate_age(r.birthdate)
durations = [1,4,30,60]
distances = [100,500,1000,2000,5000,6000,10000,21097,42195]
print r.weightcategory,r.sex,age,'aap'
for distance in distances:
worldclasspower = metrics.getagegrouprecord(
age,
sex=r.sex,
distance=distance,
weightcategory=r.weightcategory
)
velo = (worldclasspower/2.8)**(1./3.)
duration = distance/velo
wcdurations.append(duration)
wcpower.append(worldclasspower)
for duration in durations:
worldclasspower = metrics.getagegrouprecord(
age,
sex=r.sex,
duration=duration,
weightcategory=r.weightcategory
)
wcdurations.append(60.*duration)
velo = (worldclasspower/2.8)**(1./3.)
distance = int(60*duration*velo)
wcpower.append(worldclasspower)
else:
worldclasspower = None
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
# get all indoor rows in date range
# process form
if request.method == 'POST' and "daterange" in request.POST:
dateform = DateRangeForm(request.POST)
deltaform = DeltaDaysForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
if startdate > enddate:
s = enddate
enddate = startdate
startdate = s
elif request.method == 'POST' and "datedelta" in request.POST:
deltaform = DeltaDaysForm(request.POST)
if deltaform.is_valid():
deltadays = deltaform.cleaned_data['deltadays']
if deltadays:
enddate = timezone.now()
startdate = enddate-datetime.timedelta(days=deltadays)
if startdate > enddate:
s = enddate
enddate = startdate
startdate = s
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
else:
dateform = DateRangeForm()
deltaform = DeltaDaysForm()
else:
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
deltaform = DeltaDaysForm()
# get all 2k (if any) - this rower, in date range
try:
r = getrower(theuser)
except Rower.DoesNotExist:
allergworkouts = []
r=0
try:
uu = User.objects.get(id=theuser)
except User.DoesNotExist:
uu = ''
# test to fix bug
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
enddate = enddate+datetime.timedelta(days=1)
rankingdistances = [100,500,1000,2000,5000,6000,10000,21097,42195,100000]
rankingdurations = []
rankingdurations.append(datetime.time(minute=1))
rankingdurations.append(datetime.time(minute=4))
rankingdurations.append(datetime.time(minute=30))
rankingdurations.append(datetime.time(hour=1,minute=15))
rankingdurations.append(datetime.time(hour=1))
thedistances = []
theworkouts = []
thesecs = []
rankingdistances.sort()
rankingdurations.sort()
for rankingdistance in rankingdistances:
workouts = Workout.objects.filter(user=r,distance=rankingdistance,
workouttype__in=['rower','dynamic','slides'],
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by('duration')
if workouts:
thedistances.append(rankingdistance)
theworkouts.append(workouts[0])
timesecs = 3600*workouts[0].duration.hour
timesecs += 60*workouts[0].duration.minute
timesecs += workouts[0].duration.second
timesecs += 1.e-6*workouts[0].duration.microsecond
thesecs.append(timesecs)
for rankingduration in rankingdurations:
workouts = Workout.objects.filter(user=r,duration=rankingduration,
workouttype='rower',
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by('-distance')
if workouts:
thedistances.append(workouts[0].distance)
theworkouts.append(workouts[0])
timesecs = 3600*workouts[0].duration.hour
timesecs += 60*workouts[0].duration.minute
timesecs += workouts[0].duration.second
timesecs += 1.e-5*workouts[0].duration.microsecond
thesecs.append(timesecs)
thedistances = np.array(thedistances)
thesecs = np.array(thesecs)
thevelos = thedistances/thesecs
theavpower = 2.8*(thevelos**3)
# create interactive plot
if len(thedistances) !=0 :
res = interactive_cpchart(
r,thedistances,thesecs,theavpower,
theworkouts,promember=promember,
wcdurations=wcdurations,wcpower=wcpower
)
script = res[0]
div = res[1]
paulslope = res[2]
paulintercept = res[3]
p1 = res[4]
message = res[5]
else:
script = ''
div = '<p>No ranking pieces found.</p>'
paulslope = 1
paulintercept = 1
p1 = [1,1,1,1]
message = ""
if request.method == 'POST' and "piece" in request.POST:
form = PredictedPieceForm(request.POST)
if form.is_valid():
value = form.cleaned_data['value']
hourvalue,value = divmod(value,60)
if hourvalue >= 24:
hourvalue = 23
pieceunit = form.cleaned_data['pieceunit']
if pieceunit == 'd':
rankingdistances.append(value)
else:
rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue)))
else:
form = PredictedPieceForm()
rankingdistances.sort()
rankingdurations.sort()
predictions = []
cpredictions = []
for rankingdistance in rankingdistances:
# Paul's model
p = paulslope*np.log10(rankingdistance)+paulintercept
velo = 500./p
t = rankingdistance/velo
pwr = 2.8*(velo**3)
a = {'distance':rankingdistance,
'duration':timedeltaconv(t),
'pace':timedeltaconv(p),
'power':int(pwr)}
predictions.append(a)
# CP model -
pwr2 = p1[0]/(1+t/p1[2])
pwr2 += p1[1]/(1+t/p1[3])
if pwr2 <= 0:
pwr2 = 50.
velo2 = (pwr2/2.8)**(1./3.)
if np.isnan(velo2) or velo2 <= 0:
velo2 = 1.0
t2 = rankingdistance/velo2
pwr3 = p1[0]/(1+t2/p1[2])
pwr3 += p1[1]/(1+t2/p1[3])
if pwr3 <= 0:
pwr3 = 50.
velo3 = (pwr3/2.8)**(1./3.)
if np.isnan(velo3) or velo3 <= 0:
velo3 = 1.0
t3 = rankingdistance/velo3
p3 = 500./velo3
a = {'distance':rankingdistance,
'duration':timedeltaconv(t3),
'pace':timedeltaconv(p3),
'power':int(pwr3)}
cpredictions.append(a)
for rankingduration in rankingdurations:
t = 3600.*rankingduration.hour
t += 60.*rankingduration.minute
@@ -10920,3 +11270,39 @@ def team_members_stats_view(request,id):
})
return response
from rowers.models import C2WorldClassAgePerformance
def agegrouprecordview(request,sex='male',weightcategory='hwt',
distance=2000,duration=None):
if not duration:
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
distance=distance,
sex=sex,
weightcategory=weightcategory
).values()
)
)
else:
duration = int(duration)*60
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
duration=duration,
sex=sex,
weightcategory=weightcategory
).values()
)
)
script,div = interactive_agegroup_plot(df)
return render(request, 'agegroupchart.html',
{
'interactiveplot':script,
'the_div':div,
})