From 849d9cdaf313178dd85f89e0aead85f562f48039 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 14 Oct 2020 21:24:37 +0200
Subject: [PATCH] working predictions and fit type
---
rowers/forms.py | 11 ++++
rowers/interactiveplots.py | 8 ++-
rowers/templates/otwcp.html | 20 +++++++
rowers/templates/user_analysis_select.html | 35 ++++++++++--
rowers/views/analysisviews.py | 66 +++++++++++++++++++---
5 files changed, 126 insertions(+), 14 deletions(-)
create mode 100644 rowers/templates/otwcp.html
diff --git a/rowers/forms.py b/rowers/forms.py
index 3bfbe8ce..6d387abb 100644
--- a/rowers/forms.py
+++ b/rowers/forms.py
@@ -1108,6 +1108,17 @@ class AnalysisChoiceForm(forms.Form):
required=False,
label='Include Rest Strokes')
+ cpfitchoices = (
+ ('data','Fit to Selected Workouts'),
+ ('automatic','Critical Power Rolling Data')
+ )
+
+ cpfit = forms.ChoiceField(choices=cpfitchoices,
+ label = 'Model Fit',initial='data',required=False)
+
+ piece = forms.IntegerField(initial=4,label='Ranking Piece (minutes)',
+ required=False)
+
class BoxPlotChoiceForm(forms.Form):
yparam = forms.ChoiceField(choices=parchoices,initial='spm',
label='Metric')
diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py
index 56deef4b..3f78a6f4 100644
--- a/rowers/interactiveplots.py
+++ b/rowers/interactiveplots.py
@@ -2881,7 +2881,7 @@ def interactive_agegroupcpchart(age,normalized=False):
return script,div
-def interactive_otwcpchart(powerdf,promember=0,rowername=""):
+def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data'):
powerdf = powerdf[~(powerdf == 0).any(axis=1)]
# plot tools
if (promember==1):
@@ -2906,7 +2906,13 @@ def interactive_otwcpchart(powerdf,promember=0,rowername=""):
thesecs = powerdf['Delta']
theavpower = powerdf['CP']
+
p1,fitt,fitpower,ratio = datautils.cpfit(powerdf)
+ if cpfit == 'automatic' and r is not None:
+ p1 = [r.p0,r.p1,r.p2,r.p3]
+ ratio = r.cpratio
+ fitfunc = lambda pars,x: abs(pars[0])/(1+(x/abs(pars[2]))) + abs(pars[1])/(1+(x/abs(pars[3])))
+ fitpower = fitfunc(p1,fitt)
message = ""
#if len(fitpower[fitpower<0]) > 0:
diff --git a/rowers/templates/otwcp.html b/rowers/templates/otwcp.html
new file mode 100644
index 00000000..798ba395
--- /dev/null
+++ b/rowers/templates/otwcp.html
@@ -0,0 +1,20 @@
+
+ {{ the_div|safe }}
+
+
+
+
+ | Duration |
+ Power Estimate 1 |
+ Power Estimate 2 |
+
+
+
+
+ | {{ duration }} |
+ {{ power }} |
+ {{ upper }} |
+
+
+
+
diff --git a/rowers/templates/user_analysis_select.html b/rowers/templates/user_analysis_select.html
index a3afc68b..b70e44f4 100644
--- a/rowers/templates/user_analysis_select.html
+++ b/rowers/templates/user_analysis_select.html
@@ -89,6 +89,9 @@
var plottype = $("#id_plottype").parent().parent();
var reststrokes = $("#id_includereststrokes").parent().parent();
+ var piece = $("#id_piece").parent().parent();
+ var cpfit = $("#id_cpfit").parent().parent();
+
// Hide the fields.
// Use JS to do this in case the user doesn't have JS
@@ -109,6 +112,8 @@
workmax.hide();
spmmin.hide();
spmmax.hide();
+ cpfit.hide();
+ piece.hide();
if (functionfield.val() == 'boxplot') {
plotfield.show();
@@ -141,8 +146,14 @@
xaxis.show();
yaxis1.show();
plottype.show();
+ piece.hide();
}
+ if (functionfield.val() == 'cp') {
+ cpfit.show();
+ piece.show();
+ }
+
// Setup an event listener for when the state of the
// checkbox changes.
@@ -169,6 +180,8 @@
yaxis2.hide();
plottype.hide();
reststrokes.show();
+ cpfit.hide();
+ piece.hide();
}
else if (Value=='histo') {
plotfield.show();
@@ -187,6 +200,8 @@
yaxis2.hide();
plottype.hide();
reststrokes.show();
+ cpfit.hide();
+ piece.hide();
}
else if (Value=='trendflex') {
@@ -206,6 +221,8 @@
yaxis2.hide();
plottype.hide();
reststrokes.show();
+ cpfit.hide();
+ piece.hide();
}
else if (Value=='flexall') {
@@ -225,6 +242,8 @@
plottype.hide();
errorbars.hide();
reststrokes.show();
+ cpfit.hide();
+ piece.hide();
}
else if (Value=='stats') {
xaxis.hide();
@@ -239,6 +258,8 @@
errorbars.hide();
plottype.hide();
reststrokes.show();
+ cpfit.hide();
+ piece.hide();
}
else if (Value=='compare') {
xaxis.show();
@@ -256,7 +277,11 @@
binsize.hide();
plottype.show();
errorbars.hide();
+ piece.hide();
+ cpfit.hide();
reststrokes.show();
+
+
}
else if (Value=='cp') {
plotfield.hide();
@@ -275,6 +300,8 @@
yaxis2.hide();
plottype.hide();
reststrokes.hide();
+ cpfit.show();
+ piece.show();
}
});
});
@@ -299,9 +326,9 @@
-
-
- {{ the_div|safe }}
-
+
+ {{ the_div|safe }}
+
-
You can use the date and search forms to search through all
@@ -327,7 +354,7 @@
-
- {{ searchform }}
+
{{ searchform }}
{% if workouts %}
diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py
index aa8cce9d..d0ea82e2 100644
--- a/rowers/views/analysisviews.py
+++ b/rowers/views/analysisviews.py
@@ -274,6 +274,8 @@ def trendflexdata(workouts, options,userid=0):
workmin = options['workmin']
workmax = options['workmax']
ploterrorbars = options['ploterrorbars']
+ cpfit = options['cpfit']
+ piece = options['piece']
ids = options['ids']
workstrokesonly = not includereststrokes
@@ -502,7 +504,7 @@ def histodata(workouts, options):
def cpdata(workouts, options):
userid = options['userid']
-
+ cpfit = options['cpfit']
u = User.objects.get(id=userid)
r = u.rower
@@ -527,17 +529,18 @@ def cpdata(workouts, options):
rowername = r.user.first_name+" "+r.user.last_name
if len(powerdf) !=0 :
- res = interactive_otwcpchart(powerdf,promember=True,rowername=rowername)
+ res = interactive_otwcpchart(powerdf,promember=True,rowername=rowername,r=r,
+ cpfit=cpfit)
script = res[0]
div = res[1]
p1 = res[2]
ratio = res[3]
- r.p0 = p1[0]
- r.p1 = p1[1]
- r.p2 = p1[2]
- r.p3 = p1[3]
- r.cpratio = ratio
- r.save()
+ #r.p0 = p1[0]
+ #r.p1 = p1[1]
+ #r.p2 = p1[2]
+ #r.p3 = p1[3]
+ #r.cpratio = ratio
+ #r.save()
paulslope = 1
paulintercept = 1
message = res[4]
@@ -547,12 +550,57 @@ def cpdata(workouts, options):
paulslope = 1
paulintercept = 1
p1 = [1,1,1,1]
+ ratio = 1
message = ""
scripta = script.split('\n')[2:-1]
script = ''.join(scripta)
- return (script,div)
+ minutes = options['piece']
+ if minutes != 0:
+ hourvalue,tvalue = divmod(minutes,60)
+ hourvalue = int(hourvalue)
+ minutevalue = int(tvalue)
+ tvalue = int(60*(tvalue-minutevalue))
+ if hourvalue >= 24:
+ hourvalue = 23
+ pieceduration = datetime.time(
+ minute = minutevalue,
+ hour = hourvalue,
+ second = tvalue,
+ )
+ pieceseconds = 2600.*pieceduration.hour+60.*pieceduration.minute+pieceduration.second
+ # CP model
+ pwr = p1[0]/(1+pieceseconds/p1[2])
+ pwr += p1[1]/(1+pieceseconds/p1[3])
+
+ if pwr <= 0:
+ pwr = 50.
+
+ if not np.isnan(pwr):
+ try:
+ pwr2 = pwr*ratio
+ except:
+ pwr2 = pwr
+
+ duration = timedeltaconv(pieceseconds)
+ power = int(pwr)
+ upper = int(pwr2)
+ else:
+ duration = timedeltaconv(0)
+ power = 0
+ upper = 0
+
+ htmly = env.get_template('otwcp.html')
+ html_content = htmly.render({
+ 'script':script,
+ 'the_div':div,
+ 'duration':duration,
+ 'power':power,
+ 'upper':upper,
+ })
+
+ return (script,html_content)
def statsdata(workouts, options):
includereststrokes = options['includereststrokes']