added notification to rowing physics
This commit is contained in:
@@ -36,7 +36,7 @@ import pandas as pd
|
||||
import numpy as np
|
||||
import itertools
|
||||
import math
|
||||
from tasks import handle_sendemail_unrecognized
|
||||
from tasks import handle_sendemail_unrecognized,handle_sendemail_breakthrough
|
||||
|
||||
from django.conf import settings
|
||||
from sqlalchemy import create_engine
|
||||
@@ -44,6 +44,7 @@ import sqlalchemy as sa
|
||||
import sys
|
||||
|
||||
import utils
|
||||
import datautils
|
||||
from utils import lbstoN
|
||||
from scipy.interpolate import griddata
|
||||
|
||||
@@ -140,107 +141,6 @@ def filter_df(datadf,fieldname,value,largerthan=True):
|
||||
|
||||
return datadf
|
||||
|
||||
def getsinglecp(df):
|
||||
thesecs = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min()
|
||||
if thesecs != 0:
|
||||
maxt = 2*thesecs
|
||||
else:
|
||||
maxt = 1000.
|
||||
|
||||
maxlog10 = np.log10(maxt)
|
||||
logarr = np.arange(50)*maxlog10/50.
|
||||
logarr = [int(10.**(la)) for la in logarr]
|
||||
logarr = pd.Series(logarr)
|
||||
logarr.drop_duplicates(keep='first',inplace=True)
|
||||
|
||||
logarr = logarr.values
|
||||
|
||||
|
||||
dfnew = pd.DataFrame({
|
||||
'time':df['TimeStamp (sec)']-df.ix[0,'TimeStamp (sec)'],
|
||||
'power':df[' Power (watts)']
|
||||
})
|
||||
|
||||
dfnew['workoutid'] = 0
|
||||
|
||||
dfgrouped = dfnew.groupby(['workoutid'])
|
||||
delta,cpvalue,avgpower = getcp(dfgrouped,logarr)
|
||||
|
||||
return delta,cpvalue,avgpower
|
||||
|
||||
def getcp(dfgrouped,logarr):
|
||||
delta = []
|
||||
cpvalue = []
|
||||
avgpower = {}
|
||||
#avgpower[0] = 0
|
||||
|
||||
for id,group in dfgrouped:
|
||||
tt = group['time'].copy()
|
||||
ww = group['power'].copy()
|
||||
|
||||
tmax = tt.max()
|
||||
newlen = int(tmax/5000.)
|
||||
if newlen < len(tt):
|
||||
newt = np.arange(newlen)*tmax/float(newlen)
|
||||
ww = griddata(tt.values,
|
||||
ww.values,
|
||||
newt,method='linear',
|
||||
rescale=True)
|
||||
|
||||
tt = pd.Series(newt)
|
||||
ww = pd.Series(ww)
|
||||
|
||||
try:
|
||||
avgpower[id] = int(ww.mean())
|
||||
except ValueError:
|
||||
avgpower[id] = '---'
|
||||
if not np.isnan(ww.mean()):
|
||||
length = len(ww)
|
||||
dt = []
|
||||
cpw = []
|
||||
for i in xrange(length-2):
|
||||
deltat,wmax = getmaxwattinterval(tt,ww,i)
|
||||
if not np.isnan(deltat) and not np.isnan(wmax):
|
||||
dt.append(deltat)
|
||||
cpw.append(wmax)
|
||||
|
||||
|
||||
|
||||
dt = pd.Series(dt)
|
||||
cpw = pd.Series(cpw)
|
||||
if len(dt):
|
||||
|
||||
cpvalues = griddata(dt.values,
|
||||
cpw.values,
|
||||
logarr,method='linear',
|
||||
rescale=True)
|
||||
|
||||
for cpv in cpvalues:
|
||||
cpvalue.append(cpv)
|
||||
for d in logarr:
|
||||
delta.append(d)
|
||||
|
||||
delta = pd.Series(delta,name='Delta')
|
||||
cpvalue = pd.Series(cpvalue,name='CP')
|
||||
return delta,cpvalue,avgpower
|
||||
|
||||
def getmaxwattinterval(tt,ww,i):
|
||||
w_roll = ww.rolling(i+2).mean().dropna()
|
||||
if len(w_roll):
|
||||
# now goes with # data points - should be fixed seconds
|
||||
indexmax = w_roll.idxmax(axis=1)
|
||||
try:
|
||||
t_0 = tt.ix[indexmax]
|
||||
t_1 = tt.ix[indexmax-i]
|
||||
deltat = 1.0e-3*(t_0-t_1)
|
||||
wmax = w_roll.ix[indexmax]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
wmax = 0
|
||||
deltat = 0
|
||||
|
||||
return deltat,wmax
|
||||
|
||||
def df_resample(datadf):
|
||||
# time stamps must be in seconds
|
||||
@@ -527,7 +427,7 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
|
||||
isbreakthrough = False
|
||||
if workouttype == 'water':
|
||||
delta,cpvalues,avgpower = getsinglecp(row.df)
|
||||
delta,cpvalues,avgpower = datautils.getsinglecp(row.df)
|
||||
if utils.isbreakthrough(delta,cpvalues,r.p0,r.p1,r.p2,r.p3):
|
||||
isbreakthrough = True
|
||||
|
||||
@@ -689,16 +589,21 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
# submit email task to send email about breakthrough workout
|
||||
if isbreakthrough:
|
||||
a_messages.info(r.user,'It looks like you have a new breakthrough workout')
|
||||
if settings.DEBUG:
|
||||
res = handle_sendemail_breakthrough(w.id,r.user.email,
|
||||
if settings.DEBUG and r.getemailnotifications:
|
||||
res = handle_sendemail_breakthrough.delay(w.id,r.user.email,
|
||||
r.user.first_name,
|
||||
r.user.last_name)
|
||||
elif r.getemailnotifications:
|
||||
try:
|
||||
res = queuehigh.enqueue(
|
||||
handle_sendemail_breakthrough(w.id,
|
||||
r.user.email,
|
||||
r.user.first_name,
|
||||
r.user.last_name))
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
res = queuehigh.enqueue(
|
||||
handle_sendemail_breakthrough(w.id,
|
||||
r.user.email,
|
||||
r.user.first_name,
|
||||
r.user.last_name))
|
||||
pass
|
||||
|
||||
if privacy == 'visible':
|
||||
ts = Team.objects.filter(rower=r)
|
||||
|
||||
109
rowers/datautils.py
Normal file
109
rowers/datautils.py
Normal file
@@ -0,0 +1,109 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from scipy.interpolate import griddata
|
||||
|
||||
def getsinglecp(df):
|
||||
thesecs = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min()
|
||||
if thesecs != 0:
|
||||
maxt = 2*thesecs
|
||||
else:
|
||||
maxt = 1000.
|
||||
|
||||
maxlog10 = np.log10(maxt)
|
||||
logarr = np.arange(50)*maxlog10/50.
|
||||
logarr = [int(10.**(la)) for la in logarr]
|
||||
logarr = pd.Series(logarr)
|
||||
logarr.drop_duplicates(keep='first',inplace=True)
|
||||
|
||||
logarr = logarr.values
|
||||
|
||||
|
||||
dfnew = pd.DataFrame({
|
||||
'time':1000*(df['TimeStamp (sec)']-df.ix[0,'TimeStamp (sec)']),
|
||||
'power':df[' Power (watts)']
|
||||
})
|
||||
|
||||
dfnew['workoutid'] = 0
|
||||
|
||||
dfgrouped = dfnew.groupby(['workoutid'])
|
||||
delta,cpvalue,avgpower = getcp(dfgrouped,logarr)
|
||||
|
||||
return delta,cpvalue,avgpower
|
||||
|
||||
def getcp(dfgrouped,logarr):
|
||||
delta = []
|
||||
cpvalue = []
|
||||
avgpower = {}
|
||||
#avgpower[0] = 0
|
||||
|
||||
for id,group in dfgrouped:
|
||||
tt = group['time'].copy()
|
||||
ww = group['power'].copy()
|
||||
|
||||
tmax = tt.max()
|
||||
if tmax > 500000:
|
||||
newlen = int(tmax/5000.)
|
||||
else:
|
||||
newlen = len(tt)
|
||||
if newlen < len(tt):
|
||||
newt = np.arange(newlen)*tmax/float(newlen)
|
||||
ww = griddata(tt.values,
|
||||
ww.values,
|
||||
newt,method='linear',
|
||||
rescale=True)
|
||||
|
||||
tt = pd.Series(newt)
|
||||
ww = pd.Series(ww)
|
||||
|
||||
|
||||
try:
|
||||
avgpower[id] = int(ww.mean())
|
||||
except ValueError:
|
||||
avgpower[id] = '---'
|
||||
if not np.isnan(ww.mean()):
|
||||
length = len(ww)
|
||||
dt = []
|
||||
cpw = []
|
||||
for i in xrange(length-2):
|
||||
deltat,wmax = getmaxwattinterval(tt,ww,i)
|
||||
if not np.isnan(deltat) and not np.isnan(wmax):
|
||||
dt.append(deltat)
|
||||
cpw.append(wmax)
|
||||
|
||||
|
||||
|
||||
dt = pd.Series(dt)
|
||||
cpw = pd.Series(cpw)
|
||||
if len(dt):
|
||||
|
||||
cpvalues = griddata(dt.values,
|
||||
cpw.values,
|
||||
logarr,method='linear',
|
||||
rescale=True)
|
||||
|
||||
for cpv in cpvalues:
|
||||
cpvalue.append(cpv)
|
||||
for d in logarr:
|
||||
delta.append(d)
|
||||
|
||||
delta = pd.Series(delta,name='Delta')
|
||||
cpvalue = pd.Series(cpvalue,name='CP')
|
||||
return delta,cpvalue,avgpower
|
||||
|
||||
def getmaxwattinterval(tt,ww,i):
|
||||
w_roll = ww.rolling(i+2).mean().dropna()
|
||||
if len(w_roll):
|
||||
# now goes with # data points - should be fixed seconds
|
||||
indexmax = w_roll.idxmax(axis=1)
|
||||
try:
|
||||
t_0 = tt.ix[indexmax]
|
||||
t_1 = tt.ix[indexmax-i]
|
||||
deltat = 1.0e-3*(t_0-t_1)
|
||||
wmax = w_roll.ix[indexmax]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
wmax = 0
|
||||
deltat = 0
|
||||
|
||||
return deltat,wmax
|
||||
@@ -232,6 +232,9 @@ class Rower(models.Model):
|
||||
('hidden','Hidden'),
|
||||
)
|
||||
|
||||
getemailnotifications = models.BooleanField(default=True,
|
||||
verbose_name='Receive email notifications')
|
||||
|
||||
rowerplan = models.CharField(default='basic',max_length=30,
|
||||
choices=plans)
|
||||
|
||||
@@ -746,7 +749,7 @@ class RowerPowerZonesForm(ModelForm):
|
||||
class AccountRowerForm(ModelForm):
|
||||
class Meta:
|
||||
model = Rower
|
||||
fields = ['weightcategory']
|
||||
fields = ['weightcategory','getemailnotifications']
|
||||
|
||||
class UserForm(ModelForm):
|
||||
class Meta:
|
||||
|
||||
@@ -69,6 +69,8 @@ def handle_sendemail_breakthrough(workoutid,useremail,userfirstname,userlastname
|
||||
message += str(workoutid)
|
||||
message += "/updatecp\n\n"
|
||||
|
||||
message += "To opt out of these email notifications, deselect the checkbox on your Profile page under Account Information.\n\n"
|
||||
|
||||
message += "Best Regards, the Rowsandall Team"
|
||||
|
||||
email = EmailMessage(subject, message,
|
||||
@@ -79,7 +81,6 @@ def handle_sendemail_breakthrough(workoutid,useremail,userfirstname,userlastname
|
||||
res = email.send()
|
||||
|
||||
# remove tcx file
|
||||
os.remove(unrecognizedfile)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -177,7 +178,7 @@ def handle_sendemailcsv(first_name,last_name,email,csvfile):
|
||||
# Calculate wind and stream corrections for OTW rowing
|
||||
@app.task
|
||||
def handle_otwsetpower(f1,boattype,weightvalue,
|
||||
first_name,last_name,email,workoutid,
|
||||
first_name,last_name,email,workoutid,ps=[1,1,1,1],
|
||||
debug=False):
|
||||
try:
|
||||
rowdata = rdata(f1)
|
||||
@@ -219,6 +220,12 @@ def handle_otwsetpower(f1,boattype,weightvalue,
|
||||
rowdata.write_csv(f1,gzip=True)
|
||||
update_strokedata(workoutid,rowdata.df,debug=debug)
|
||||
|
||||
delta,cpvalues,avgpower = datautils.getsinglecp(rowdata.df)
|
||||
if utils.isbreakthrough(delta,cpvalues,ps[0],ps[1],ps[2],ps[3]):
|
||||
handle_sendemail_breakthrough(workoutid,email,
|
||||
first_name,
|
||||
last_name)
|
||||
|
||||
# send email
|
||||
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
|
||||
subject = "Your Rowsandall OTW calculations are ready"
|
||||
|
||||
@@ -68,6 +68,9 @@ class C2Objects(DjangoTestCase):
|
||||
u = User.objects.create_user('john',
|
||||
'sander@ds.ds',
|
||||
'koeinsloot')
|
||||
u.first_name = 'John'
|
||||
u.last_name = 'Sander'
|
||||
u.save()
|
||||
r = Rower.objects.create(user=u)
|
||||
|
||||
res = add_workout_from_strokedata(u,1,data,strokedata,source='c2')
|
||||
@@ -88,6 +91,9 @@ class C2Objects(DjangoTestCase):
|
||||
u = User.objects.create_user('john',
|
||||
'sander@ds.ds',
|
||||
'koeinsloot')
|
||||
u.first_name = 'John'
|
||||
u.last_name = 'Sander'
|
||||
u.save()
|
||||
r = Rower.objects.create(user=u)
|
||||
|
||||
res = add_workout_from_strokedata(u,1,data,strokedata,source='c2')
|
||||
@@ -162,6 +168,9 @@ class StravaObjects(DjangoTestCase):
|
||||
u = User.objects.create_user('john',
|
||||
'sander@ds.ds',
|
||||
'koeinsloot')
|
||||
u.first_name = 'John'
|
||||
u.last_name = 'Sander'
|
||||
u.save()
|
||||
r = Rower.objects.create(user=u)
|
||||
|
||||
res = add_workout_from_strokedata(u,1,workoutsummary,strokedata,
|
||||
@@ -235,6 +244,9 @@ class StravaObjects(DjangoTestCase):
|
||||
u = User.objects.create_user('john',
|
||||
'sander@ds.ds',
|
||||
'koeinsloot')
|
||||
u.first_name = 'John'
|
||||
u.last_name = 'Sander'
|
||||
u.save()
|
||||
r = Rower.objects.create(user=u)
|
||||
|
||||
res = add_workout_from_strokedata(u,1,workoutsummary,strokedata,
|
||||
|
||||
@@ -183,6 +183,7 @@ urlpatterns = [
|
||||
url(r'^workout/compare/(?P<id>\d+)/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\w+.*)$',views.workout_comparison_list),
|
||||
url(r'^workout/(?P<id>\d+)/edit$',views.workout_edit_view),
|
||||
url(r'^workout/(?P<id>\d+)/setprivate$',views.workout_setprivate_view),
|
||||
url(r'^workout/(?P<id>\d+)/updatecp$',views.workout_update_cp_view),
|
||||
url(r'^workout/(?P<id>\d+)/makepublic$',views.workout_makepublic_view),
|
||||
url(r'^workout/(?P<id>\d+)/geeky$',views.workout_geeky_view),
|
||||
url(r'^workout/(?P<id>\d+)/advanced$',views.workout_advanced_view),
|
||||
|
||||
@@ -77,6 +77,7 @@ def geo_distance(lat1,lon1,lat2,lon2):
|
||||
|
||||
|
||||
def isbreakthrough(delta,cpvalues,p0,p1,p2,p3):
|
||||
|
||||
pwr = p0/(1+delta/p2)
|
||||
pwr += p1/(1+delta/p3)
|
||||
|
||||
|
||||
@@ -274,6 +274,8 @@ from utils import (
|
||||
str2bool
|
||||
)
|
||||
|
||||
import datautils
|
||||
|
||||
from rowers.models import checkworkoutuser
|
||||
|
||||
# Check if a user is a Coach member
|
||||
@@ -2778,6 +2780,27 @@ def rankings_view(request,theuser=0,
|
||||
'teams':get_my_teams(request.user),
|
||||
})
|
||||
|
||||
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
||||
def workout_update_cp_view(request,id=0):
|
||||
try:
|
||||
row = Workout.objects.get(id=id)
|
||||
except Workout.DoesNotExist:
|
||||
raise Http404("Workout doesn't exist")
|
||||
|
||||
if (checkworkoutuser(request.user,row)==False):
|
||||
message = "You are not allowed to edit this workout"
|
||||
messages.error(request,message)
|
||||
url = reverse(workouts_view)
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
row.rankingpiece = True
|
||||
row.save()
|
||||
|
||||
url = reverse(otwrankings_view)
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Show ranking distances including predicted paces
|
||||
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
||||
def otwrankings_view(request,theuser=0,
|
||||
@@ -2919,7 +2942,7 @@ def otwrankings_view(request,theuser=0,
|
||||
|
||||
|
||||
dfgrouped = df.groupby(['workoutid'])
|
||||
delta,cpvalue,avgpower = dataprep.getcp(dfgrouped,logarr)
|
||||
delta,cpvalue,avgpower = datautils.getcp(dfgrouped,logarr)
|
||||
|
||||
|
||||
powerdf = pd.DataFrame({
|
||||
@@ -7779,6 +7802,7 @@ def rower_edit_view(request,message=""):
|
||||
last_name = ucd['last_name']
|
||||
email = ucd['email']
|
||||
weightcategory = cd['weightcategory']
|
||||
getemailnotifications = cd['getemailnotifications']
|
||||
u = request.user
|
||||
if len(first_name):
|
||||
u.first_name = first_name
|
||||
@@ -7788,6 +7812,7 @@ def rower_edit_view(request,message=""):
|
||||
u.save()
|
||||
r = getrower(u)
|
||||
r.weightcategory = weightcategory
|
||||
r.getemailnotifications = getemailnotifications
|
||||
r.save()
|
||||
form = RowerForm(instance=r)
|
||||
powerform = RowerPowerForm(instance=r)
|
||||
|
||||
Reference in New Issue
Block a user