Private
Public Access
1
0

Merge branch 'feature/powertimefitnessmetric' into develop

This commit is contained in:
Sander Roosendaal
2018-01-17 21:33:18 +01:00
10 changed files with 258 additions and 5 deletions
+17
View File
@@ -515,6 +515,23 @@ def paceformatsecs(values):
return out return out
def fitnessmetric_to_sql(m,table='powertimefitnessmetric',debug=False):
engine = create_engine(database_url, echo=False)
columns = ', '.join(m.keys())
placeholders = ", ".join(["?"] * len(m))
query = "INSERT into %s ( %s ) Values (%s)" % (table, columns, placeholders)
values = tuple(m[key] for key in m.keys())
with engine.connect() as conn, conn.begin():
result = conn.execute(query,values)
conn.close()
engine.dispose()
return 1
def getcpdata_sql(rower_id,table='cpdata'): def getcpdata_sql(rower_id,table='cpdata'):
engine = create_engine(database_url, echo=False) engine = create_engine(database_url, echo=False)
query = sa.text('SELECT * from {table} WHERE user={rower_id};'.format( query = sa.text('SELECT * from {table} WHERE user={rower_id};'.format(
+20
View File
@@ -495,6 +495,26 @@ def getsmallrowdata_db(columns,ids=[],debug=False):
return data return data
def fitnessmetric_to_sql(m,table='powertimefitnessmetric',debug=False):
if debug:
engine = create_engine(database_url_debug, echo=False)
else:
engine = create_engine(database_url, echo=False)
columns = ', '.join(m.keys())
placeholders = ", ".join(["?"] * len(m))
query = "INSERT into %s ( %s ) Values (%s)" % (table, columns, placeholders)
values = tuple(m[key] for key in m.keys())
with engine.connect() as conn, conn.begin():
result = conn.execute(query,values)
conn.close()
engine.dispose()
return 1
+2 -1
View File
@@ -300,7 +300,8 @@ def getmaxwattinterval(tt,ww,i):
wmax = 0 wmax = 0
deltat = 0 deltat = 0
except KeyError: except KeyError:
pass wmax = 0
deltat = 0
else: else:
wmax = 0 wmax = 0
deltat = 0 deltat = 0
+70
View File
@@ -0,0 +1,70 @@
from django.utils import timezone
from rowers.models import Workout, PowerTimeFitnessMetric, Rower
import datetime
from utils import myqueue
import django_rq
queue = django_rq.get_queue('default')
from rowers.tasks import handle_updatefitnessmetric
def getrower(user):
try:
r = Rower.objects.get(user=user)
except Rower.DoesNotExist:
r = Rower(user=user)
r.save()
return r
def do_update(user,mode='rower',days=42):
r = getrower(user)
startdate = timezone.now()-datetime.timedelta(days=days)
# test if not something already done
now_date = timezone.now().strftime('%Y-%m-%d')
ms = PowerTimeFitnessMetric.objects.filter(
user=user,
workoutmode=mode)
if len(ms) == 0:
max_workout_id = 0
last_update_date = '1972-01-01'
else:
max_workout_id = max([m.last_workout for m in ms])
last_update_date = max([m.date.strftime('%Y-%m-%d') for m in ms])
if mode == 'rower':
workouts = Workout.objects.filter(
user=r,
workouttype__in=['rower','dynamic','slides'],
startdatetime__gte=startdate)
else:
workouts = Workout.objects.filter(
user=r,
workouttype__in=['water','coastal'],
startdatetime__gte=startdate)
theids = [int(w.id) for w in workouts]
try:
max_id = max(theids)
except ValueError:
max_id = 0
if last_update_date < now_date and max_workout_id < max_id:
job = myqueue(queue,
handle_updatefitnessmetric,
user.id,mode,theids,
)
return 1
class PowerTimeFitnessMetricMiddleWare(object):
def process_request(self, request):
# Code to be executed before the view is called
if request.user.is_authenticated():
result = do_update(request.user,mode='rower')
result = do_update(request.user,mode='water')
+4 -1
View File
@@ -248,13 +248,16 @@ class PowerTimeFitnessMetric(models.Model):
('water','On the water') ('water','On the water')
) )
data = models.DateField(default=timezone.now) date = models.DateField(default=timezone.now)
last_workout = models.IntegerField(default=0)
user = models.ForeignKey(User) user = models.ForeignKey(User)
PowerFourMin = models.FloatField(default=0) PowerFourMin = models.FloatField(default=0)
PowerTwoK = models.FloatField(default=0) PowerTwoK = models.FloatField(default=0)
PowerOneHour = models.FloatField(default=0) PowerOneHour = models.FloatField(default=0)
workoutmode = models.CharField(default='rower',choices=modechoices, workoutmode = models.CharField(default='rower',choices=modechoices,
max_length=40) max_length=40)
class Meta:
db_table = 'powertimefitnessmetric'
class C2WorldClassAgePerformance(models.Model): class C2WorldClassAgePerformance(models.Model):
weightcategories = ( weightcategories = (
+78 -1
View File
@@ -25,13 +25,14 @@ from rowsandall_app.settings import PROGRESS_CACHE_SECRET
import pandas as pd import pandas as pd
from django_rq import job from django_rq import job
from django.utils import timezone
from utils import deserialize_list from utils import deserialize_list
from rowers.dataprepnodjango import ( from rowers.dataprepnodjango import (
update_strokedata, new_workout_from_file, update_strokedata, new_workout_from_file,
getsmallrowdata_db, updatecpdata_sql, getsmallrowdata_db, updatecpdata_sql,
update_agegroup_db, update_agegroup_db,fitnessmetric_to_sql
) )
from django.core.mail import send_mail, EmailMessage from django.core.mail import send_mail, EmailMessage
@@ -612,6 +613,82 @@ def handle_updateergcp(rower_id,workoutfilenames,debug=False,**kwargs):
return 1 return 1
@app.task
def handle_updatefitnessmetric(user_id,mode,workoutids,debug=False,
**kwargs):
columns = ['power','workoutid','time']
df = getsmallrowdata_db(columns,ids=workoutids,debug=debug)
df.dropna(inplace=True,axis=0)
if df.empty:
# change this
return 2
# df is not empty. We continue
dfgrouped = df.groupby(['workoutid'])
maxt = 1.05*df['time'].max()/1000.
logarr = datautils.getlogarr(maxt)
delta,cpvalue,avgpower = datautils.getcp(dfgrouped,logarr)
powerdf = pd.DataFrame({
'Delta':delta,
'CP':cpvalue,
})
powerdf = powerdf[powerdf['CP']>0]
powerdf.dropna(axis=0,inplace=True)
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
p1,fitt,fitpower,ratio = datautils.cpfit(powerdf)
# This is code duplication from datautils -- correct asap
fitfunc = lambda pars,x: abs(pars[0])/(1+(x/abs(pars[2]))) + abs(pars[1])/(1+(x/abs(pars[3])))
powerfourmin = fitfunc(p1,240.)
powerhour = fitfunc(p1,3600.)
# 2k power
velofourmin = (powerfourmin/2.8)**(1./3.)
dfourmin = 240.*velofourmin
dratio = 2000./dfourmin
pacefourmin = 500./velofourmin
# assume 5 sec per doubling drop
pace2k = pacefourmin + 5.*np.log10(dratio)/np.log10(2.)
velo2k = 500./pace2k
t2k = 2000./velo2k
pwr2k = fitfunc(p1,t2k)
velo2 = (pwr2k/2.8)**(1./3.)
if np.isnan(velo2) or velo2 <= 0:
velo2 = 1.0
t2 = 2000./velo2
pwr2k = fitfunc(p1,t2)
velo3 = (pwr2k/2.8)**(1./3.)
t3 = 2000./velo3
power2k = fitfunc(p1,t3)
mdict = {
'user_id': user_id,
'PowerFourMin': powerfourmin,
'PowerTwoK': power2k,
'PowerOneHour': powerhour,
'workoutmode': mode,
'last_workout': max(workoutids),
'date': timezone.now().strftime('%Y-%m-%d'),
}
result = fitnessmetric_to_sql(mdict,debug=debug)
return result
@app.task @app.task
def handle_updatecp(rower_id,workoutids,debug=False,table='cpdata',**kwargs): def handle_updatecp(rower_id,workoutids,debug=False,table='cpdata',**kwargs):
+3
View File
@@ -125,6 +125,9 @@ urlpatterns = [
url(r'^agegroupcp/(?P<age>\d+)/(?P<normalize>\d+)$',views.agegroupcpview), url(r'^agegroupcp/(?P<age>\d+)/(?P<normalize>\d+)$',views.agegroupcpview),
url(r'^ajax_agegroup/(?P<age>\d+)/(?P<weightcategory>\w+.*)/(?P<sex>\w+.*)/(?P<userid>\d+)$', url(r'^ajax_agegroup/(?P<age>\d+)/(?P<weightcategory>\w+.*)/(?P<sex>\w+.*)/(?P<userid>\d+)$',
views.ajax_agegrouprecords), views.ajax_agegrouprecords),
url(r'^updatefitness/(?P<mode>\w+.*)/(?P<days>\d+)$',views.fitness_metric_view),
url(r'^updatefitness/(?P<mode>\w+.*)$',views.fitness_metric_view),
url(r'^updatefitness$',views.fitness_metric_view),
url(r'^agegrouprecords/(?P<sex>\w+.*)/(?P<weightcategory>\w+.*)/(?P<distance>\d+)m$', url(r'^agegrouprecords/(?P<sex>\w+.*)/(?P<weightcategory>\w+.*)/(?P<distance>\d+)m$',
views.agegrouprecordview), views.agegrouprecordview),
url(r'^agegrouprecords/(?P<sex>\w+.*)/(?P<weightcategory>\w+.*)/(?P<duration>\d+)min$', url(r'^agegrouprecords/(?P<sex>\w+.*)/(?P<weightcategory>\w+.*)/(?P<duration>\d+)min$',
+1
View File
@@ -15,6 +15,7 @@ landingpages = (
) )
workflowmiddlepanel = ( workflowmiddlepanel = (
('panel_statcharts.html','Static Charts'), ('panel_statcharts.html','Static Charts'),
('flexthumbnails.html','Flex Charts'), ('flexthumbnails.html','Flex Charts'),
+62 -2
View File
@@ -56,7 +56,7 @@ from rowers.models import (
RowerPowerZonesForm,AccountRowerForm,UserForm,StrokeData, RowerPowerZonesForm,AccountRowerForm,UserForm,StrokeData,
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest, Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
WorkoutComment,WorkoutCommentForm,RowerExportForm, WorkoutComment,WorkoutCommentForm,RowerExportForm,
CalcAgePerformance CalcAgePerformance,PowerTimeFitnessMetric,
) )
from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement
from rowers.metrics import rowingmetrics,defaultfavoritecharts from rowers.metrics import rowingmetrics,defaultfavoritecharts
@@ -109,7 +109,8 @@ from rowers.tasks import (
handle_sendemail_unrecognized,handle_sendemailnewcomment, handle_sendemail_unrecognized,handle_sendemailnewcomment,
handle_sendemailnewresponse, handle_updatedps, handle_sendemailnewresponse, handle_updatedps,
handle_updatecp,long_test_task,long_test_task2, handle_updatecp,long_test_task,long_test_task2,
handle_zip_file,handle_getagegrouprecords handle_zip_file,handle_getagegrouprecords,
handle_updatefitnessmetric
) )
from scipy.signal import savgol_filter from scipy.signal import savgol_filter
@@ -159,6 +160,20 @@ class JSONResponse(HttpResponse):
kwargs['content_type'] = 'application/json' kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs) super(JSONResponse, self).__init__(content, **kwargs)
def getrower(user):
try:
r = Rower.objects.get(user=user)
except Rower.DoesNotExist:
r = Rower(user=user)
r.save()
return r
def getvalue(data): def getvalue(data):
perc = 0 perc = 0
total = 1 total = 1
@@ -238,6 +253,7 @@ from io import BytesIO
from scipy.special import lambertw from scipy.special import lambertw
from dataprep import timedeltaconv from dataprep import timedeltaconv
from dataprep import getsmallrowdata_db
from scipy.interpolate import griddata from scipy.interpolate import griddata
@@ -736,6 +752,8 @@ def iscoachmember(user):
return result return result
def getrower(user): def getrower(user):
try: try:
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
@@ -745,6 +763,8 @@ def getrower(user):
return r return r
# Check if a user is a Pro member # Check if a user is a Pro member
def ispromember(user): def ispromember(user):
if not user.is_anonymous(): if not user.is_anonymous():
@@ -3206,6 +3226,46 @@ def addmanual_view(request):
{'form':form, {'form':form,
}) })
@login_required()
def fitness_metric_view(request,mode='rower',days=42):
r = getrower(request.user)
startdate = timezone.now()-datetime.timedelta(days=days)
# test if not something already done
ms = PowerTimeFitnessMetric.objects.filter(user=request.user)
max_workout_id = max([m.last_workout for m in ms])
last_update_date = max([m.date.strftime('%Y-%m-%d') for m in ms])
now_date = timezone.now().strftime('%Y-%m-%d')
if mode == 'rower':
workouts = Workout.objects.filter(
user=r,
workouttype__in=['rower','dynamic','slides'],
startdatetime__gte=startdate)
else:
workouts = Workout.objects.filter(
user=r,
workouttype__in=['water','coastal'],
startdatetime__gte=startdate)
theids = [int(w.id) for w in workouts]
max_id = max(theids)
if last_update_date >= now_date or max_workout_id >= max_id:
return HttpResponse("already done today or no new workouts")
job = myqueue(queue,
handle_updatefitnessmetric,
request.user.id,mode,theids,
)
return HttpResponse("job queued")
# Show ranking distances including predicted paces # Show ranking distances including predicted paces
@login_required() @login_required()
def rankings_view(request,theuser=0, def rankings_view(request,theuser=0,
+1
View File
@@ -93,6 +93,7 @@ MIDDLEWARE_CLASSES = [
'async_messages.middleware.AsyncMiddleware', 'async_messages.middleware.AsyncMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'tz_detect.middleware.TimezoneMiddleware', 'tz_detect.middleware.TimezoneMiddleware',
'rowers.middleware.PowerTimeFitnessMetricMiddleWare',
] ]
ROOT_URLCONF = 'rowsandall_app.urls' ROOT_URLCONF = 'rowsandall_app.urls'