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']