Merge branch 'feature/c2agerecords' into develop
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = (
|
||||
|
||||
47
rowers/templates/agegroupchart.html
Normal file
47
rowers/templates/agegroupchart.html
Normal 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 %}
|
||||
@@ -79,8 +79,13 @@
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_6 alpha">
|
||||
<p> </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">
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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))
|
||||
|
||||
388
rowers/views.py
388
rowers/views.py
@@ -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,
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user