From 29243516ae3e383afe893812ceed270d04f9b83c Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 26 Oct 2017 22:53:02 +0200
Subject: [PATCH] does Alternative OTE ranking
added an OTE ranking piece calculator based on manually added ranking
instead of automatically detected ranking
---
rowers/.#views.py | 1 +
rowers/dataprep.py | 43 ++++--
rowers/models.py | 14 ++
rowers/tasks.py | 4 +-
rowers/templates/analysis.html | 16 ++-
rowers/templates/otwrankings.html | 4 +-
rowers/urls.py | 7 +-
rowers/views.py | 224 +++++++++++++++++++++++++++++-
8 files changed, 295 insertions(+), 18 deletions(-)
create mode 100644 rowers/.#views.py
diff --git a/rowers/.#views.py b/rowers/.#views.py
new file mode 100644
index 00000000..f675534b
--- /dev/null
+++ b/rowers/.#views.py
@@ -0,0 +1 @@
+E408191@CZ27LT9RCGN72.344848:1509049169
\ No newline at end of file
diff --git a/rowers/dataprep.py b/rowers/dataprep.py
index e51e6631..04fe66ff 100644
--- a/rowers/dataprep.py
+++ b/rowers/dataprep.py
@@ -473,21 +473,37 @@ def updatecpdata_sql(rower_id,delta,cp,table='cpdata',distance=[]):
engine.dispose()
-def runcpupdate(rower):
+def runcpupdate(rower,type='water'):
startdate = timezone.now()-datetime.timedelta(days=365)
enddate = timezone.now()+datetime.timedelta(days=5)
- theworkouts = Workout.objects.filter(user=rower,rankingpiece=True,
- workouttype='water',
- startdatetime__gte=startdate,
- startdatetime__lte=enddate)
+ if type == 'water':
+ theworkouts = Workout.objects.filter(
+ user=rower,rankingpiece=True,
+ workouttype='water',
+ startdatetime__gte=startdate,
+ startdatetime__lte=enddate
+ )
+ table = 'cpdata'
+ else:
+ theworkouts = Workout.objects.filter(
+ user=rower,rankingpiece=True,
+ workouttype__in=[
+ 'rower',
+ 'dynamic',
+ 'slides'
+ ],
+ startdatetime__gte=startdate,
+ startdatetime__lte=enddate
+ )
+ table = 'cpergdata'
theids = [w.id for w in theworkouts]
if settings.DEBUG:
- res = handle_updatecp.delay(rower.id,theids,debug=True)
+ res = handle_updatecp.delay(rower.id,theids,debug=True,table=table)
else:
- res = queue.enqueue(handle_updatecp,rower.id,theids)
+ res = queue.enqueue(handle_updatecp,rower.id,theids,table=table)
def fetchcperg(rower,theworkouts):
theids = [int(w.id) for w in theworkouts]
@@ -502,24 +518,29 @@ def fetchcperg(rower,theworkouts):
return cpdf
-def fetchcp(rower,theworkouts):
+def fetchcp(rower,theworkouts,table='cpdata'):
# get all power data from database (plus workoutid)
theids = [int(w.id) for w in theworkouts]
columns = ['power','workoutid','time']
df = getsmallrowdata_db(columns,ids=theids)
+ if df.empty:
+ avgpower2 = {}
+ for id in theids:
+ avgpower2[id] = 0
+ return pd.Series([]),pd.Series([]),avgpower2
dfgrouped = df.groupby(['workoutid'])
avgpower2 = dict(dfgrouped.mean()['power'].astype(int))
- cpdf = getcpdata_sql(rower.id)
+ cpdf = getcpdata_sql(rower.id,table=table)
if not cpdf.empty:
return cpdf['delta'],cpdf['cp'],avgpower2
else:
if settings.DEBUG:
- res = handle_updatecp.delay(rower.id,theids,debug=True)
+ res = handle_updatecp.delay(rower.id,theids,debug=True,table=table)
else:
- res = queue.enqueue(handle_updatecp,rower.id,theids)
+ res = queue.enqueue(handle_updatecp,rower.id,theids,table=table)
return [],[],avgpower2
diff --git a/rowers/models.py b/rowers/models.py
index 1b233404..bfc190cc 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -653,6 +653,20 @@ class cpdata(models.Model):
index_together = ['user']
app_label = 'rowers'
+
+# Storing data for the OTW CP chart
+class cpergdata(models.Model):
+ delta = models.IntegerField(default=0)
+ cp = models.FloatField(default=0)
+ user = models.IntegerField(default=0)
+
+ class Meta:
+ db_table = 'cpergdata'
+ index_together = ['user']
+ app_label = 'rowers'
+
+
+
# Storing data for the OTW CP chart
class ergcpdata(models.Model):
delta = models.IntegerField(default=0)
diff --git a/rowers/tasks.py b/rowers/tasks.py
index 372dc1cb..bbb3c88f 100644
--- a/rowers/tasks.py
+++ b/rowers/tasks.py
@@ -420,7 +420,7 @@ def handle_updateergcp(rower_id,workoutfilenames,debug=False):
@app.task
-def handle_updatecp(rower_id,workoutids,debug=False):
+def handle_updatecp(rower_id,workoutids,debug=False,table='cpdata'):
columns = ['power','workoutid','time']
df = getsmallrowdata_db(columns,ids=workoutids,debug=debug)
dfgrouped = df.groupby(['workoutid'])
@@ -435,7 +435,7 @@ def handle_updatecp(rower_id,workoutids,debug=False):
delta,cpvalue,avgpower = datautils.getcp(dfgrouped,logarr)
- updatecpdata_sql(rower_id,delta,cpvalue,debug=debug)
+ updatecpdata_sql(rower_id,delta,cpvalue,debug=debug,table=table)
return 1
diff --git a/rowers/templates/analysis.html b/rowers/templates/analysis.html
index 6fa3f882..4c8d41da 100644
--- a/rowers/templates/analysis.html
+++ b/rowers/templates/analysis.html
@@ -92,7 +92,7 @@
{% endif %}
- Analyse power vs piece duration to make predictions.
+ Analyse power vs piece duration to make predictions. For On-The-Water rowing.
@@ -120,6 +120,20 @@
+
+
+
+ {% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
+ OTE Ranking Pieces
+ {% else %}
+ OTE Ranking Pieces
+ {% endif %}
+
+
+ Analyse power vs piece duration to make predictions, for erg pieces.
+
+
+
diff --git a/rowers/templates/otwrankings.html b/rowers/templates/otwrankings.html
index 871d79ff..d31eed1c 100644
--- a/rowers/templates/otwrankings.html
+++ b/rowers/templates/otwrankings.html
@@ -196,8 +196,8 @@
minutes
-
diff --git a/rowers/urls.py b/rowers/urls.py
index d33c32b6..5ba765db 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -150,7 +150,12 @@ urlpatterns = [
url(r'^otw-bests/(?P\w+.*)/(?P\w+.*)$',views.otwrankings_view),
url(r'^otw-bests/(?P\d+)$',views.otwrankings_view),
url(r'^otw-bests/$',views.otwrankings_view),
- url(r'^(?P\d+)/otw-bests/$',views.otwrankings_view),
+ url(r'^(?P\d+)/ote-ranking/(?P\w+.*)/(?P\w+.*)$',views.oterankings_view),
+ url(r'^(?P\d+)/ote-ranking/(?P\d+)$',views.oterankings_view),
+ url(r'^ote-ranking/(?P\w+.*)/(?P\w+.*)$',views.oterankings_view),
+ url(r'^ote-ranking/(?P\d+)$',views.oterankings_view),
+ url(r'^ote-ranking/$',views.oterankings_view),
+ url(r'^(?P\d+)/ote-ranking/$',views.oterankings_view),
url(r'^(?P\d+)/flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)$',views.cum_flex),
url(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)$',views.cum_flex),
url(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)$',views.cum_flex),
diff --git a/rowers/views.py b/rowers/views.py
index e128caf9..d5acc762 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -3091,6 +3091,228 @@ def otwrankings_view(request,theuser=0,
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]
+ ratio = res[3]
+ 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]
+ else:
+ script = ''
+ div = 'No ranking pieces found.
'
+ 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)
+ hourvalue = int(hourvalue)
+ minutevalue = int(value)
+ value = int(60*(value-minutevalue))
+ if hourvalue >= 24:
+ hourvalue = 23
+ rankingdurations.append(datetime.time(minute=minutevalue,
+ hour=hourvalue,
+ second=value))
+ else:
+ form = PredictedPieceForm()
+
+
+ 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):
+ try:
+ pwr2 = pwr*ratio
+ except:
+ pwr2 = pwr
+
+ a = {
+ 'duration':timedeltaconv(t),
+ 'power':int(pwr),
+ 'upper':int(pwr2)}
+ cpredictions.append(a)
+
+
+ del form.fields["pieceunit"]
+
+ messages.error(request,message)
+ return render(request, 'otwrankings.html',
+ {'rankingworkouts':theworkouts,
+ 'interactiveplot':script,
+ 'the_div':div,
+ 'cpredictions':cpredictions,
+ 'avgpower':avgpower,
+ 'form':form,
+ 'dateform':dateform,
+ 'deltaform':deltaform,
+ 'id': theuser,
+ 'theuser':uu,
+ 'startdate':startdate,
+ 'enddate':enddate,
+ 'teams':get_my_teams(request.user),
+ })
+# Show ranking distances including predicted paces
+@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
+def oterankings_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))
+ rankingdurations.append(datetime.time(hour=1,minute=15))
+
+ thedistances = []
+ theworkouts = []
+ thesecs = []
+
+ theworkouts = Workout.objects.filter(user=r,rankingpiece=True,
+ workouttype__in=[
+ 'rower',
+ 'dynamic',
+ 'slides'
+ ],
+ startdatetime__gte=startdate,
+ startdatetime__lte=enddate)
+
+
+ delta,cpvalue,avgpower = dataprep.fetchcp(
+ r,theworkouts,table='cpergdata'
+ )
+
+ powerdf = pd.DataFrame({
+ 'Delta':delta,
+ 'CP':cpvalue,
+ })
+
+ if powerdf.empty:
+ messages.info(request,'Your calculations are running in the background. Please reload this page.')
+
+ 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)
@@ -6630,7 +6852,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
successmessage = "Changes saved"
if rankingpiece:
- dataprep.runcpupdate(row.user)
+ dataprep.runcpupdate(row.user,type=row.workouttype)
messages.info(request,successmessage)
url = reverse(workout_edit_view,