Private
Public Access
1
0

Merge branch 'feature/duplicatesprivate' into develop

This commit is contained in:
Sander Roosendaal
2017-06-08 14:24:46 +02:00
12 changed files with 697 additions and 28 deletions
+1
View File
File diff suppressed because one or more lines are too long
+11 -5
View File
@@ -398,7 +398,8 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
notes='',totaldist=0,totaltime=0,
summary='',
makeprivate=False,
oarlength=2.89,inboard=0.88):
oarlength=2.89,inboard=0.88,
consistencychecks=True):
message = None
powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1,
@@ -417,10 +418,13 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
for key,value in checks.iteritems():
if not value:
allchecks = 0
a_messages.error(r.user,'Failed consistency check: '+key+', autocorrected')
if consistencychecks:
a_messages.error(r.user,'Failed consistency check: '+key+', autocorrected')
else:
a_messages.error(r.user,'Failed consistency check: '+key+', not corrected')
if not allchecks:
#row.repair()
if not allchecks and consistencychecks:
# row.repair()
pass
@@ -529,6 +533,7 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
user=r)
if (len(ws) != 0):
message = "Warning: This workout probably already exists in the database"
privacy = 'private'
# checking for inf values
totaldist = np.nan_to_num(totaldist)
@@ -781,7 +786,8 @@ def new_workout_from_df(r,df,
oarlength=oarlength,
inboard=inboard,
makeprivate=makeprivate,
dosmooth=False)
dosmooth=False,
consistencychecks=False)
return (id,message)
+1
View File
@@ -243,6 +243,7 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
user=r)
if (len(ws) != 0):
message = "Warning: This workout probably already exists in the database"
privacy = 'private'
+100
View File
@@ -596,6 +596,106 @@ def googlemap_chart(lat,lon,name=""):
return [script,div]
def interactive_otwcpchart(powerdf,promember=0):
powerdf = powerdf[~(powerdf == 0).any(axis=1)]
# plot tools
if (promember==1):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
else:
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
x_axis_type = 'log'
y_axis_type = 'linear'
deltas = powerdf['Delta'].apply(lambda x: timedeltaconv(x))
powerdf['ftime'] = niceformat(deltas)
source = ColumnDataSource(
data = powerdf
)
# there is no Paul's law for OTW
# Fit the data to thee 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]
p1 = p0
thesecs = powerdf['Delta']
theavpower = powerdf['CP']
if len(thesecs)>=4:
p1, success = optimize.leastsq(errfunc, p0[:], args = (thesecs,theavpower))
else:
factor = fitfunc(p0,thesecs.mean())/theavpower.mean()
p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]]
fitt = pd.Series(10**(4*np.arange(100)/100.))
fitpower = fitfunc(p1,fitt)
message = ""
#if len(fitpower[fitpower<0]) > 0:
# message = "CP model fit didn't give correct results"
sourcecomplex = ColumnDataSource(
data = dict(
power = fitpower,
duration = fitt
)
)
# making the plot
plot = Figure(tools=TOOLS,x_axis_type=x_axis_type,
plot_width=900,
toolbar_location="above",
toolbar_sticky=False)
# add watermark
plot.extra_y_ranges = {"watermark": watermarkrange}
plot.image_url([watermarkurl],1.8*max(thesecs),watermarky,
watermarkw,watermarkh,
global_alpha=watermarkalpha,
w_units='screen',
h_units='screen',
anchor=watermarkanchor,
dilate=True,
y_range_name = "watermark",
)
plot.circle('Delta','CP',source=source,fill_color='red',size=15,
legend='Power Data')
plot.xaxis.axis_label = "Duration (seconds)"
plot.yaxis.axis_label = "Power (W)"
plot.y_range = Range1d(0,1.5*max(theavpower))
plot.x_range = Range1d(1,2*max(thesecs))
plot.legend.orientation = "vertical"
hover = plot.select(dict(type=HoverTool))
hover.tooltips = OrderedDict([
('Duration ','@ftime'),
('Power (W)','@CP{int}'),
])
hover.mode = 'mouse'
plot.line('duration','power',source=sourcecomplex,legend="CP Model",
color='green')
script, div = components(plot)
return [script,div,p1,message]
def interactive_cpchart(thedistances,thesecs,theavpower,
theworkouts,promember=0):
+3 -2
View File
@@ -425,7 +425,8 @@ class Workout(models.Model):
summary = models.TextField(blank=True)
privacy = models.CharField(default='visible',max_length=30,
choices=privacychoices)
rankingpiece = models.BooleanField(default=False,verbose_name='Ranking Piece')
def __unicode__(self):
date = self.date
@@ -552,7 +553,7 @@ class WorkoutForm(ModelForm):
duration = forms.TimeInput(format='%H:%M:%S.%f')
class Meta:
model = Workout
fields = ['name','date','starttime','duration','distance','workouttype','notes','privacy','boattype']
fields = ['name','date','starttime','duration','distance','workouttype','notes','privacy','rankingpiece','boattype']
widgets = {
'date': DateInput(),
'notes': forms.Textarea,
+16 -12
View File
@@ -245,7 +245,7 @@ def get_strava_workout(user,stravaid):
return [workoutsummary,df]
# Generate Workout data for Strava (a TCX file)
def createstravaworkoutdata(w):
def createstravaworkoutdata(w,dozip=True):
filename = w.csvfilename
row = rowingdata(filename)
@@ -256,18 +256,22 @@ def createstravaworkoutdata(w):
newnotes = 'from '+w.workoutsource+' via rowsandall.com'
row.exporttotcx(tcxfilename,notes=newnotes)
gzfilename = tcxfilename+'.gz'
with file(tcxfilename,'rb') as inF:
s = inF.read()
with gzip.GzipFile(gzfilename,'wb') as outF:
outF.write(s)
if dozip:
gzfilename = tcxfilename+'.gz'
with file(tcxfilename,'rb') as inF:
s = inF.read()
with gzip.GzipFile(gzfilename,'wb') as outF:
outF.write(s)
try:
os.remove(tcxfilename)
except WindowError:
pass
return gzfilename,""
try:
os.remove(tcxfilename)
except WindowError:
pass
return gzfilename,""
else:
return tcxfilename,""
# Upload the TCX file to Strava and set the workout activity type
+20
View File
@@ -77,8 +77,28 @@
</div>
</div>
<div class="grid_12 alpha">
<div class="grid_6 alpha">
<p>&nbsp;</p>
</div>
<div class="grid_6 omega">
<div class="grid_2 suffix_4 alpha">
<p>
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<a class="button blue small" href="/rowers/otw-bests">OTW Ranking Pieces</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">OTW Ranking Pieces</a>
{% endif %}
</p>
<p>
Analyse power vs piece duration to make predictions.
</p>
</div>
</div>
</div>
{% endblock %}
+4
View File
@@ -147,6 +147,10 @@
</div>
{% endif %}
<div class="grid_1 omega">
<a href="/rowers/workout/{{ workout.id }}/emailgpx">
<img src="/static/img/gpx.jpg" alt="GPX Export" width="60" height="60"></a>
</div>
</div>
</div>
+207
View File
@@ -0,0 +1,207 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Workouts{% 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, true, 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="title" class="grid_12 alpha">
<div class="grid_10 alpha">
{% if theuser %}
<h3>{{ theuser.first_name }}'s Ranking Pieces</h3>
{% else %}
<h3>{{ user.first_name }}'s Ranking Pieces</h3>
{% endif %}
</div>
<div class="grid_2 omega">
{% if user.is_authenticated and user|is_manager %}
<div class="grid_2 alpha dropdown">
<button class="grid_2 alpha button green small dropbtn">
Change Rower
</button>
<div class="dropdown-content">
{% for member in user|team_members %}
<a class="button green small" href="/rowers/{{ member.id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">{{ member.first_name }} {{ member.last_name }}</a>
{% endfor %}
</div>
{% else %}
&nbsp;
{% endif %}
</div>
</div>
<div id="summary" class="grid_6 alpha">
<p>Summary for {{ theuser.first_name }} {{ theuser.last_name }}
between {{ startdate|date }} and {{ enddate|date }}</p>
<p>Direct link for other users:
<a href="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">https://rowsandall.com/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
</p>
<p>The table gives the OTW efforts you marked as Ranking Piece.
The graph shows the best segments from those pieces, plotted as
average power (over the segment) vs the duration of the segment/
In other words: How long you can hold that power.
</p>
<p>At the bottom of the page, you will find predictions derived from the model.</p>
</div>
<div id="form" class="grid_6 omega">
<p>Use this form to select a different date range:</p>
<p>
Select start and end date for a date range:
<div class="grid_4 alpha">
<form enctype="multipart/form-data" action="" method="post">
<table>
{{ dateform.as_table }}
</table>
{% csrf_token %}
</div>
<div class="grid_2 omega">
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
</div>
<div class="grid_4 alpha">
<form enctype="multipart/form-data" action="" method="post">
Or use the last {{ deltaform }} days.
</div>
<div class="grid_2 omega">
{% csrf_token %}
<input name='datedelta' class="button green" type="submit" value="Submit">
</form>
</div>
</div>
<div id="theplot" class="grid_12 alpha">
<h2>Critical Power Plot</h2>
{{ the_div|safe }}
</div>
<div class="grid_12 alpha">
<h2>Ranking Piece Results</h2>
{% if rankingworkouts %}
<table width="70%" class="listtable">
<thead>
<tr>
<th> Distance</th>
<th> Duration</th>
<th> Avg Power</th>
<th> Date</th>
<th> Avg HR </th>
<th> Max HR </th>
<th> Edit</th>
<tr>
</thead>
<tbody>
{% for workout in rankingworkouts %}
<tr>
<td> {{ workout.distance }} m</td>
<td> {{ workout.duration |durationprint:"%H:%M:%S.%f" }} </td>
<td> {{ avgpower|lookup:workout.id }} W</td>
<td> {{ workout.date }} </td>
<td> {{ workout.averagehr }} </td>
<td> {{ workout.maxhr }} </td>
<td>
<a href="/rowers/workout/{{ workout.id }}/edit">{{ workout.name }}</a> </td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p> No ranking workouts found </p>
{% endif %}
</div>
<div id="predictions" class="grid_12 alpha">
<h2>Pace predictions for Ranking Pieces</h2>
<p>Add non-ranking piece using the form. The piece will be added in the prediction tables below. </p>
<div id="cpmodel" class="grid_6 alpha">
<table width="70%" class="listtable">
<thead>
<tr>
<th> Duration</th>
<th> Power </th>
<tr>
</thead>
<tbody>
{% for pred in cpredictions %}
<tr>
{% for key, value in pred.items %}
{% if key == "power" %}
<td> {{ value }} W </td>
{% endif %}
{% if key == "duration" %}
<td> {{ value |deltatimeprint }} </td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="grid_3">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{{ form.value }} {{ form.pieceunit }}
{% csrf_token %}
</div>
<div class="grid_1">
minutes
</div>
<div class="grid_2 omega">
<input name="piece" class="button green"
formaction="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}"
type="submit" value="Add">
</form>
</div>
</div>
{% endblock %}
+7
View File
@@ -135,6 +135,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+)/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),
url(r'^otw-bests/(?P<deltadays>\d+)$',views.otwrankings_view),
url(r'^otw-bests/$',views.otwrankings_view),
url(r'^(?P<theuser>\d+)/otw-bests/$',views.otwrankings_view),
url(r'^(?P<theuser>\d+)/flexall/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.cum_flex),
url(r'^flexall/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.cum_flex),
url(r'^flexall/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)$',views.cum_flex),
@@ -169,6 +175,7 @@ urlpatterns = [
url(r'^workout/(?P<id>\d+)/export$',views.workout_export_view),
url(r'^workout/(?P<id>\d+)/comment$',views.workout_comment_view),
url(r'^workout/(?P<id>\d+)/emailtcx$',views.workout_tcxemail_view),
url(r'^workout/(?P<id>\d+)/emailgpx$',views.workout_gpxemail_view),
url(r'^workout/(?P<id>\d+)/emailcsv$',views.workout_csvemail_view),
url(r'^workout/(?P<id>\d+)/csvtoadmin$',views.workout_csvtoadmin_view),
url(r'^workout/compare/(?P<id>\d+)/$',views.workout_comparison_list),
+327 -9
View File
@@ -138,6 +138,8 @@ from scipy.special import lambertw
from dataprep import timedeltaconv
from scipy.interpolate import griddata
LOCALTIMEZONE = tz('Etc/UTC')
USER_LANGUAGE = 'en-US'
@@ -290,14 +292,11 @@ def iscoachmember(user):
return result
def getrower(user):
if not user.is_anonymous():
try:
r = Rower.objects.get(user=user)
except Rower.DoesNotExist:
r = Rower(user=user)
r.save()
else:
raise PermissionDenied("You need to log in to use this function")
try:
r = Rower.objects.get(user=user)
except Rower.DoesNotExist:
r = Rower(user=user)
r.save()
return r
@@ -1086,7 +1085,7 @@ def workout_tcxemail_view(request,id=0):
raise Http404("Workout doesn't exist")
if (checkworkoutuser(request.user,w)):
try:
tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w)
tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w,dozip=False)
if tcxfile == 0:
message = "Something went wrong (TCX export) "+tcxmessg
messages.error(request,message)
@@ -1138,6 +1137,51 @@ def workout_tcxemail_view(request,id=0):
return response
# Export workout to GPX and send to user's email address
@login_required()
def workout_gpxemail_view(request,id=0):
message = ""
successmessage = ""
r = Rower.objects.get(user=request.user)
try:
w = Workout.objects.get(id=id)
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
if (checkworkoutuser(request.user,w)):
filename = w.csvfilename
row = rdata(filename)
gpxfilename = filename[:-4]+'.gpx'
row.exporttogpx(gpxfilename)
if settings.DEBUG:
res = handle_sendemailtcx.delay(r.user.first_name,
r.user.last_name,
r.user.email,gpxfilename)
else:
res = queuehigh.enqueue(handle_sendemailtcx,r.user.first_name,
r.user.last_name,
r.user.email,gpxfilename)
successmessage = "The GPX file was sent to you per email"
messages.info(request,successmessage)
url = reverse(workout_export_view,
kwargs = {
'id':str(w.id),
})
response = HttpResponseRedirect(url)
else:
message = "You are not allowed to export this workout"
messages.error(request,message)
url = reverse(workout_export_view,
kwargs = {
'id':str(w.id),
})
response = HttpResponseRedirect(url)
return response
# Get Workout CSV file and send it to user's email address
@login_required()
def workout_csvemail_view(request,id=0):
@@ -2730,6 +2774,274 @@ def rankings_view(request,theuser=0,
'teams':get_my_teams(request.user),
})
# Show ranking distances including predicted paces
@login_required()
def otwrankings_view(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 = Rower.objects.get(user=request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
# get all OTW 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 = Rower.objects.get(user=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)
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))
thedistances = []
theworkouts = []
thesecs = []
theworkouts = Workout.objects.filter(user=r,rankingpiece=True,
workouttype='water',
startdatetime__gte=startdate,
startdatetime__lte=enddate)
# get all power data from database (plus workoutid)
theids = [w.id for w in theworkouts]
columns = ['power','workoutid','time']
df = dataprep.getsmallrowdata_db(columns,ids=theids)
thesecs = []
for w in theworkouts:
timesecs = 3600*w.duration.hour
timesecs += 60*w.duration.minute
timesecs += w.duration.second
timesecs += 1.e-5*w.duration.microsecond
thesecs.append(timesecs)
if len(thesecs) != 0:
maxt = pd.Series(thesecs).max()
else:
maxt = 1000.
maxlog10 = np.log10(maxt)
logarr = np.arange(100)*maxlog10/100.
logarr = [int(10.**(la)) for la in logarr]
logarr = pd.Series(logarr)
logarr.drop_duplicates(keep='first',inplace=True)
logarr = logarr.values
delta = []
cpvalue = []
avgpower = {}
dfgrouped = df.groupby(['workoutid'])
for id,group in dfgrouped:
tt = group['time']
ww = group['power']
try:
avgpower[id] = int(ww.mean())
except ValueError:
avgpower[id] = '---'
if not np.isnan(ww.mean()):
length = len(ww)
dt = []
cpw = []
for i in range(length-2):
w_roll = ww.rolling(i+2,min_periods=2).mean()
# 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-2]
deltat = 1.0e-3*(t_0-t_1)
wmax = w_roll.ix[indexmax]
dt.append(deltat)
cpw.append(wmax)
except KeyError:
pass
dt = pd.Series(dt)
cpw = pd.Series(cpw)
cpvalues = griddata(dt.values,
cpw.values,
logarr,method='linear',fill_value=0)
for cpv in cpvalues:
cpvalue.append(cpv)
for d in logarr:
delta.append(d)
print avgpower
dt = pd.Series(delta,name='Delta')
cpvalue = pd.Series(cpvalue,name='CP')
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)
# create interactive plot
if len(powerdf) !=0 :
res = interactive_otwcpchart(powerdf,promember=promember)
script = res[0]
div = res[1]
p1 = res[2]
paulslope = 1
paulintercept = 1
message = res[3]
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)
clean = form.is_valid()
value = form.cleaned_data['value']
hourvalue,value = divmod(value,60)
if hourvalue >= 24:
hourvalue = 23
rankingdurations.append(datetime.time(minute=value,hour=hourvalue))
else:
form = PredictedPieceForm()
predictions = []
cpredictions = []
for rankingduration in rankingdurations:
t = 3600.*rankingduration.hour
t += 60.*rankingduration.minute
t += rankingduration.second
t += rankingduration.microsecond/1.e6
# CP model
pwr = p1[0]/(1+t/p1[2])
pwr += p1[1]/(1+t/p1[3])
if pwr <= 0:
pwr = 50.
if not np.isnan(pwr):
a = {
'duration':timedeltaconv(t),
'power':int(pwr)}
cpredictions.append(a)
del form.fields["pieceunit"]
messages.error(request,message)
return render(request, 'otwrankings.html',
{'rankingworkouts':theworkouts,
'interactiveplot':script,
'the_div':div,
'predictions':predictions,
'cpredictions':cpredictions,
'avgpower':avgpower,
'form':form,
'dateform':dateform,
'deltaform':deltaform,
'id': theuser,
'theuser':uu,
'startdate':startdate,
'enddate':enddate,
'teams':get_my_teams(request.user),
})
# Reload the workout and calculate the summary from the stroke data (lapIDx)
@login_required()
def workout_recalcsummary_view(request,id=0):
@@ -5207,6 +5519,11 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
privacy = request.POST['privacy']
except KeyError:
privacy = Workout.objects.get(id=id).privacy
try:
rankingpiece = form.cleaned_data['rankingpiece']
except KeyError:
rankingpiece =- Workout.objects.get(id=id).rankingpiece
startdatetime = (str(date) + ' ' + str(starttime))
startdatetime = datetime.datetime.strptime(startdatetime,
"%Y-%m-%d %H:%M:%S")
@@ -5223,6 +5540,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
row.distance = distance
row.boattype = boattype
row.privacy = privacy
row.rankingpiece = rankingpiece
try:
row.save()
except IntegrityError:
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB