diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 0059162f..a68e288e 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -428,7 +428,8 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower', isbreakthrough = False if workouttype == 'water': delta,cpvalues,avgpower = datautils.getsinglecp(row.df) - if utils.isbreakthrough(delta,cpvalues,r.p0,r.p1,r.p2,r.p3,r.cpratio): + res,btvalues = utils.isbreakthrough(delta,cpvalues,r.p0,r.p1,r.p2,r.p3,r.cpratio) + if res: isbreakthrough = True res = datautils.updatecp(delta,cpvalues,r) @@ -595,14 +596,16 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower', if settings.DEBUG and r.getemailnotifications: res = handle_sendemail_breakthrough.delay(w.id,r.user.email, r.user.first_name, - r.user.last_name) + r.user.last_name, + btvalues=btvalues.to_json()) elif r.getemailnotifications: try: res = queuehigh.enqueue( handle_sendemail_breakthrough(w.id, r.user.email, r.user.first_name, - r.user.last_name)) + r.user.last_name, + btvalues=btvalues.to_json())) except AttributeError: pass else: diff --git a/rowers/datautils.py b/rowers/datautils.py index ca4cbff1..8cdde524 100644 --- a/rowers/datautils.py +++ b/rowers/datautils.py @@ -77,8 +77,8 @@ def cpfit(powerdf): def getlogarr(maxt): maxlog10 = np.log10(maxt-5) - logarr = np.log10(5.)+np.arange(50)*maxlog10/50. - logarr = [int(10.**(la)) for la in logarr] + logarr = np.arange(50)*maxlog10/50. + logarr = [5+int(10.**(la)) for la in logarr] logarr = pd.Series(logarr) logarr.drop_duplicates(keep='first',inplace=True) @@ -88,7 +88,7 @@ def getlogarr(maxt): def getsinglecp(df): thesecs = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min() if thesecs != 0: - maxt = 2*thesecs + maxt = 1.05*thesecs else: maxt = 1000. @@ -152,7 +152,6 @@ def getcp(dfgrouped,logarr): dt = pd.Series(dt) cpw = pd.Series(cpw) if len(dt): - cpvalues = griddata(dt.values, cpw.values, logarr,method='linear', diff --git a/rowers/tasks.py b/rowers/tasks.py index 03f7ece1..9b98a4e8 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -18,7 +18,8 @@ import matplotlib.pyplot as plt from matplotlib import figure import stravalib - +import pandas as pd + from django_rq import job from utils import serialize_list,deserialize_list @@ -49,7 +50,9 @@ def handle_new_workout_from_file(r,f2, # send email when a breakthrough workout is uploaded @app.task -def handle_sendemail_breakthrough(workoutid,useremail,userfirstname,userlastname): +def handle_sendemail_breakthrough(workoutid,useremail, + userfirstname,userlastname, + btvalues = pd.DataFrame().to_json()): # send email with attachment subject = "A breakthrough workout on rowsandall.com" @@ -60,7 +63,7 @@ def handle_sendemail_breakthrough(workoutid,useremail,userfirstname,userlastname message += " Critical Power (CP) is the power that you can " message += "sustain for a given duration. For more, see this " message += " article in the analytics blog:\n\n" - message += " [link to article to be written]\n\n" + message += " http://analytics.rowsandall.com/2017/06/17/how-do-we-calculate-critical-power/ \n\n" message += "Link to the workout http://rowsandall.com/rowers/workout/" message += str(workoutid) message +="/edit\n\n" @@ -69,6 +72,27 @@ def handle_sendemail_breakthrough(workoutid,useremail,userfirstname,userlastname message += str(workoutid) message += "/updatecp\n\n" + btvalues = pd.read_json(btvalues) + btvalues.sort_values('delta',axis=0,inplace=True) + + if not btvalues.empty: + message += "These were the breakthrough values:\n" + for t in btvalues.itertuples(): + delta = t.delta + cpvalue = t.cpvalues + pwr = t.pwr + + message += "Time: {delta} seconds\n".format( + delta=delta + ) + message += "New: {cpvalue:.0f} Watt\n".format( + cpvalue=cpvalue + ) + message += "Old: {pwr:.0f} Watt\n\n".format( + pwr=pwr + ) + + 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" @@ -222,10 +246,11 @@ def handle_otwsetpower(f1,boattype,weightvalue, 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],ratio): + res,btvalues = utils.isbreakthrough(delta,cpvalues,ps[0],ps[1],ps[2],ps[3],ratio) + if res: handle_sendemail_breakthrough(workoutid,email, first_name, - last_name) + last_name,btvalues=btvalues.to_json()) # send email fullemail = first_name + " " + last_name + " " + "<" + email + ">" diff --git a/rowers/templates/streamedit.html b/rowers/templates/streamedit.html index 56079e4d..1e4ba1a0 100644 --- a/rowers/templates/streamedit.html +++ b/rowers/templates/streamedit.html @@ -7,49 +7,57 @@ {% block content %}
- -

Stream Editor

-
-

- Edit Workout -

-
-
-

- Advanced Edit -

- -
-
-

OTW Power

- Run calculations to get power values for your row. - -
-
-

- Edit river Stream between turning points in your row. - Use positive (+) values to denote rowing with the stream, - negative (-) values to denote rowing against the stream. -

- +
+

Stream Editor

+
+
+
+

+ Edit Workout +

+
+
+

+ Advanced Edit +

+ +
+
+

OTW Power

+ Run calculations to get power values for your row. + +
+
+
+
+

Wind Edit

+
+
+
+

+ Edit river Stream between turning points in your row. + Use positive (+) values to denote rowing with the stream, + negative (-) values to denote rowing against the stream. +

+
- {% if form.errors %} -

- Please correct the error{{ form.errors|pluralize }} below. -

- {% endif %} - - - {{ form.as_table }} -
- {% csrf_token %} -
-
- -
- - - + {% if form.errors %} +

+ Please correct the error{{ form.errors|pluralize }} below. +

+ {% endif %} + + + {{ form.as_table }} +
+ {% csrf_token %} +
+
+ +
+ + +
@@ -86,4 +94,4 @@ {{ the_div |safe }}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/rowers/templates/windedit.html b/rowers/templates/windedit.html index 0d98cce7..79d9ce7d 100644 --- a/rowers/templates/windedit.html +++ b/rowers/templates/windedit.html @@ -6,113 +6,121 @@ {% block content %}
- -

Wind Editor

-
-

- Edit Workout -

-
-
-

- Advanced Edit -

- -
-
-

OTW Power

- Run calculations to get power values for your row. - -
-
-

- Update wind between distance 1 and distance 2. Submit wind strength - and direction at start and end of segment. Blank the form for values - you want to keep intact. -

- -

- Check www.wunderground.com - to find historical weather data from an on-line weather station near - the location of your row. -

- -
-
- {% if form.errors %} +
+

Wind Editor

+
+
+
+

+ Edit Workout +

+
+
+

+ Advanced Edit +

+ +
+
+

OTW Power

+ Run calculations to get power values for your row. + +
+
+
+ +
+
+

+ Update wind between distance 1 and distance 2. Submit wind strength + and direction at start and end of segment. Blank the form for values + you want to keep intact. +

+ +

+ Check www.wunderground.com + to find historical weather data from an on-line weather station near + the location of your row. +

+ +
+ + {% if form.errors %}

- Please correct the error{{ form.errors|pluralize }} below. + Please correct the error{{ form.errors|pluralize }} below.

- {% endif %} + {% endif %} - {{ form.as_table }} + {{ form.as_table }}
{% csrf_token %} - -
-
-

Closest Airport Weather: {{ airport }} - ({{ airportdistance | floatformat:-1 }} km) - Airport Data

-

+ +

+
+

Closest Airport Weather: {{ airport }} + ({{ airportdistance | floatformat:-1 }} km) + Airport Data

+

Dark Sky Data -

+

Download wind speed and bearing from The Dark Sky -

-
-
- -

+

+
+
+ +

Manual update of the wind data from the form. -

-
- - -
+

+
+ + +
- - - + - - {{ interactiveplot |safe }} - - {{ gmap |safe }} - - + + {{ interactiveplot |safe }} + + {{ gmap |safe }} + + - - - -
-
- {{ the_div |safe }} -
-
- -
-
- {{ gmapdiv |safe }} -
-
- + + + +
+
+ {{ the_div |safe }} +
+
+ +
+
+ {{ gmapdiv |safe }} +
+
+ {% endblock %} diff --git a/rowers/utils.py b/rowers/utils.py index 3fd67d94..6e788b7c 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -1,6 +1,6 @@ import math import numpy as np - +import pandas as pd lbstoN = 4.44822 @@ -77,7 +77,6 @@ def geo_distance(lat1,lon1,lat2,lon2): def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio): - pwr = p0/(1+delta/p2) pwr += p1/(1+delta/p3) @@ -85,4 +84,15 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio): res = np.sum(cpvalues>pwr) - return res>1 + btdf = pd.DataFrame( + { + 'delta':delta[cpvalues>pwr], + 'cpvalues':cpvalues[cpvalues>pwr], + 'pwr':pwr[cpvalues>pwr], + } + ) + + btdf.sort_values('delta',axis=0,inplace=True) + + + return res>1,btdf diff --git a/rowers/views.py b/rowers/views.py index f336270d..e61d7822 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -4403,8 +4403,9 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): rowdata.write_csv(f1,gzip=True) # do power calculation (asynchronous) - u = request.user - r = Rower.objects.get(user=u) + r = row.user + u = r.user + first_name = u.first_name last_name = u.last_name emailaddress = u.email diff --git a/rowsanda_107501 b/rowsanda_107501 index a01511d5..c0aaa646 100644 Binary files a/rowsanda_107501 and b/rowsanda_107501 differ