From b569fb2c46d63bd2ab09a229b5c0655fdeac25fb Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Tue, 16 May 2017 20:23:45 +0200
Subject: [PATCH 01/10] small improvement
---
rowers/management/commands/getexpired.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/rowers/management/commands/getexpired.py b/rowers/management/commands/getexpired.py
index b5b24e41..e6f68fc0 100644
--- a/rowers/management/commands/getexpired.py
+++ b/rowers/management/commands/getexpired.py
@@ -22,6 +22,9 @@ def getexpired():
if r.planexpires < datetime.datetime.now().date():
if r.rowerplan == 'pro' or r.rowerplan == 'coach':
lijst.append(r)
+ if r.teamplanexpires < datetime.datetime.now().date():
+ if r.rowerplan == 'pro' or r.rowerplan == 'coach':
+ lijst.append(r)
return lijst
From 791d537b8df7ccd82acae4d009ad423e45f64bd8 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 17 May 2017 09:02:14 +0200
Subject: [PATCH 02/10] added user name to paypal button field
---
rowers/templates/about_us.html | 12 ++++---
rowers/templates/paypaltest.html | 55 +++++++++++++++++++++++++++++
rowers/templates/promembership.html | 11 +++---
rowers/urls.py | 1 +
4 files changed, 70 insertions(+), 9 deletions(-)
create mode 100644 rowers/templates/paypaltest.html
diff --git a/rowers/templates/about_us.html b/rowers/templates/about_us.html
index b89f6881..5defb350 100644
--- a/rowers/templates/about_us.html
+++ b/rowers/templates/about_us.html
@@ -123,12 +123,10 @@ of a rowers technique.
Click on the PayPal button to pay for your Premium membership.
- It will be valid for one year with automatic renewal which you can stop at any time.
+ Before you pay, please register for the free Basic membership and log in.
+ In this way, your user name will be added to the payment details.
+ Your payment will be valid for one year with automatic renewal which you can stop at any time.
You will be taken to the secure PayPal payment site.
-
- - Please mention the username you are registered under in
- "instructions to seller".
-
After you do the payment, we will manually change your membership to
"Pro". Depending on our availability, this may take some time
@@ -138,6 +136,10 @@ You will be taken to the secure PayPal payment site.
diff --git a/rowers/templates/paypaltest.html b/rowers/templates/paypaltest.html
new file mode 100644
index 00000000..2183fdcf
--- /dev/null
+++ b/rowers/templates/paypaltest.html
@@ -0,0 +1,55 @@
+
+ {% extends "base.html" %}
+ {% block title %}About us{% endblock title %}
+ {% block content %}
+PayPal Test Button
+
+
+
+
This is a test page for testing the paypal functionality. Do not use it.
+
+
Donations are welcome to keep this web site going. To help cover the hosting
+costs, I have created a Pro
membership option (for only 15 EURO per year). Once I process your
+donation, I will give you access to some special
features on this
+website.
+
+
Currently, the Pro membership will give you the following extra functionality (and more will follow):
+
+ - More stroke metrics plots
+ - Power curves for OTW rowing
+ - Add weather information to OTW rowing sessions
+ - Power histogram
+
+
+
Click on the PayPal button to pay for your Pro membership. Before you pay, please register for the free Basic membership and log in.
+ In this way, your user name will be added to the payment details.
+Your payment will be valid for one year with automatic renewal which you can stop at any time.
+You will be taken to the secure PayPal payment site.
+
+
+
+
+
+
+
+
+
After you do the payment, we will manually change your membership to
+ "Pro". Depending on our availability, this may take some time
+ (typically one working day). Don't hesitate to contact us
+ if you have any questions at this stage.
+
+
If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.
+
+
+
+
+ {% endblock content %}
diff --git a/rowers/templates/promembership.html b/rowers/templates/promembership.html
index 76e228aa..4339a5d6 100644
--- a/rowers/templates/promembership.html
+++ b/rowers/templates/promembership.html
@@ -19,11 +19,10 @@ website.
Power histogram
-Click on the PayPal button to pay for your Pro membership. It will be valid for one year with automatic renewal which you can stop at any time.
+
Click on the PayPal button to pay for your Pro membership. Before you pay, please register for the free Basic membership and log in.
+ In this way, your user name will be added to the payment details.
+Your payment will be valid for one year with automatic renewal which you can stop at any time.
You will be taken to the secure PayPal payment site.
-
-- Please mention the username you are registered under in "instructions to seller".
-
@@ -31,6 +30,10 @@ You will be taken to the secure PayPal payment site.
diff --git a/rowers/urls.py b/rowers/urls.py
index 56c90669..dbb5fcd9 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -274,6 +274,7 @@ urlpatterns = [
url(r'^videos', TemplateView.as_view(template_name='videos.html'),name='videos'),
url(r'^analysis', TemplateView.as_view(template_name='analysis.html'),name='analysis'),
url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'),
+ url(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'),
url(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'),
url(r'^register$',views.rower_register_view),
url(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'),
From b7d7d5cc83442b5e424cd614385a3126dd2de6d2 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 17 May 2017 09:31:56 +0200
Subject: [PATCH 03/10] redirects to boxchart workout picker when ids messed up
---
rowers/dataprep.py | 9 +++++++++
rowers/views.py | 13 ++++++++++++-
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/rowers/dataprep.py b/rowers/dataprep.py
index eeaa2b64..e25bc255 100644
--- a/rowers/dataprep.py
+++ b/rowers/dataprep.py
@@ -95,6 +95,15 @@ from scipy.signal import savgol_filter
import datetime
+def get_workouts(ids,userid):
+ workouts = []
+ for id in ids:
+ w = Workout.objects.get(id=id)
+ if w.user.user.id == userid:
+ workouts.append[w]
+
+ return workouts
+
def filter_df(datadf,fieldname,value,largerthan=True):
try:
diff --git a/rowers/views.py b/rowers/views.py
index fb8ce778..7e6461b8 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -3124,7 +3124,18 @@ def boxplot_view(request,userid=0,
workstrokesonly = not includereststrokes
ids = request.session['ids']
request.session['ids'] = ids
- workouts = [Workout.objects.get(id=id) for id in ids]
+ workouts = dataprep.get_workouts(ids,userid)
+ if not workouts:
+ message = 'Error: Workouts in session storage do not belong to this user.'
+ messages.error(request,message)
+ url = reverse(user_boxplot_select,
+ kwargs={
+ 'userid':userid,
+ }
+ )
+ return HttpResponseRedirect(url)
+
+ # workouts = [Workout.objects.get(id=id) for id in ids]
labeldict = {
int(w.id): w.__unicode__() for w in workouts
From bb2283635e07ec93ecaeca0b68b432f0dbdb0568 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 17 May 2017 09:44:47 +0200
Subject: [PATCH 04/10] bug fix
---
rowers/dataprep.py | 8 ++++----
rowers/views.py | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/rowers/dataprep.py b/rowers/dataprep.py
index e25bc255..ba78268e 100644
--- a/rowers/dataprep.py
+++ b/rowers/dataprep.py
@@ -96,13 +96,13 @@ from scipy.signal import savgol_filter
import datetime
def get_workouts(ids,userid):
- workouts = []
+ goodids = []
for id in ids:
w = Workout.objects.get(id=id)
- if w.user.user.id == userid:
- workouts.append[w]
+ if int(w.user.user.id) == int(userid):
+ goodids.append(id)
- return workouts
+ return [Workout.objects.get(id=id) for id in goodids]
def filter_df(datadf,fieldname,value,largerthan=True):
diff --git a/rowers/views.py b/rowers/views.py
index 7e6461b8..376fa0d1 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -3111,7 +3111,7 @@ def boxplot_view(request,userid=0,
})
else:
return HttpResponse("Form is not valid")
- if request.method == 'POST' and 'ids' in request.session:
+ elif request.method == 'POST' and 'ids' in request.session:
chartform = BoxPlotChoiceForm(request.POST)
if chartform.is_valid():
plotfield = chartform.cleaned_data['yparam']
From 12d95cfe43889b1cef61a553ed767cbb10350de0 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 17 May 2017 11:54:02 +0200
Subject: [PATCH 05/10] C2 and ST button to import all new workouts
---
rowers/dataprepnodjango.py | 2 +
rowers/templates/c2_list_import2.html | 78 +++++++-----
rowers/templates/sporttracks_list_import.html | 73 ++++++-----
rowers/urls.py | 2 +
rowers/utils.py | 15 +++
rowers/views.py | 117 ++++++++++++++++--
6 files changed, 218 insertions(+), 69 deletions(-)
diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py
index 8872d4e7..af17d87a 100644
--- a/rowers/dataprepnodjango.py
+++ b/rowers/dataprepnodjango.py
@@ -18,6 +18,7 @@ from rowsandall_app.settings import DATABASES
from utils import lbstoN
+
user = DATABASES['default']['USER']
password = DATABASES['default']['PASSWORD']
database_name = DATABASES['default']['NAME']
@@ -335,6 +336,7 @@ def handle_nonpainsled(f2,fileformat,summary=''):
return (f2,summary,oarlength,inboard)
+
# Create new workout from file and store it in the database
# This routine should be used everywhere in views.py and mailprocessing.py
# Currently there is code duplication
diff --git a/rowers/templates/c2_list_import2.html b/rowers/templates/c2_list_import2.html
index 03f2d8e7..886542be 100644
--- a/rowers/templates/c2_list_import2.html
+++ b/rowers/templates/c2_list_import2.html
@@ -6,36 +6,50 @@
{% block content %}
Available on C2 Logbook
- {% if workouts %}
-
-
-
- | Import |
- Date/Time |
- Duration |
- Total Distance |
- Type |
- Source |
- Comment |
-
-
-
- {% for workout in workouts %}
-
- |
-Import |
- {{ workout|lookup:'starttime' }} |
- {{ workout|lookup:'duration' }} |
- {{ workout|lookup:'distance' }} |
- {{ workout|lookup:'rowtype' }} |
- {{ workout|lookup:'source' }} |
- {{ workout|lookup:'comment' }} |
-
-
- {% endfor %}
-
-
- {% else %}
- No workouts found
- {% endif %}
+
+{% if workouts %}
+
+
+
This imports all workouts that have not been imported to rowsandall.com.
+ The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout.
+
+
+
+
+
+
+
+ | Import |
+ Date/Time |
+ Duration |
+ Total Distance |
+ Type |
+ Source |
+ Comment |
+ New |
+
+
+
+ {% for workout in workouts %}
+
+ |
+ Import |
+ {{ workout|lookup:'starttime' }} |
+ {{ workout|lookup:'duration' }} |
+ {{ workout|lookup:'distance' }} |
+ {{ workout|lookup:'rowtype' }} |
+ {{ workout|lookup:'source' }} |
+ {{ workout|lookup:'comment' }} |
+ {{ workout|lookup:'new' }} |
+
+
+ {% endfor %}
+
+
+
+{% else %}
+ No workouts found
+{% endif %}
{% endblock %}
diff --git a/rowers/templates/sporttracks_list_import.html b/rowers/templates/sporttracks_list_import.html
index 96a2aa2d..b547efc8 100644
--- a/rowers/templates/sporttracks_list_import.html
+++ b/rowers/templates/sporttracks_list_import.html
@@ -6,34 +6,47 @@
{% block content %}
Available on SportTracks
- {% if workouts %}
-
-
-
- | Import |
- Name |
- Date/Time |
- Duration |
- Total Distance |
- Type |
-
-
-
- {% for workout in workouts %}
-
- |
-Import |
- {{ workout|lookup:'name' }} |
- {{ workout|lookup:'starttime' }} |
- {{ workout|lookup:'duration' }} |
- {{ workout|lookup:'distance' }} m |
- {{ workout|lookup:'type' }} |
-
-
- {% endfor %}
-
-
- {% else %}
- No workouts found
- {% endif %}
+{% if workouts %}
+
+
+
This imports all workouts that have not been imported to rowsandall.com.
+ The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout.
+
+
+
+
+
+
+
+ | Import |
+ Name |
+ Date/Time |
+ Duration |
+ Total Distance |
+ Type |
+ New |
+
+
+
+ {% for workout in workouts %}
+
+ |
+ Import |
+ {{ workout|lookup:'name' }} |
+ {{ workout|lookup:'starttime' }} |
+ {{ workout|lookup:'duration' }} |
+ {{ workout|lookup:'distance' }} m |
+ {{ workout|lookup:'type' }} |
+ {{ workout|lookup:'new' }} |
+
+
+ {% endfor %}
+
+
+
+{% else %}
+ No workouts found
+{% endif %}
{% endblock %}
diff --git a/rowers/urls.py b/rowers/urls.py
index dbb5fcd9..ec5d73a4 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -211,9 +211,11 @@ urlpatterns = [
url(r'^workout/c2import/$',views.workout_c2import_view),
url(r'^workout/stravaimport/$',views.workout_stravaimport_view),
url(r'^workout/c2import/(?P\d+)/$',views.workout_getc2workout_view),
+ url(r'^workout/c2import/all/$',views.workout_getc2workout_all),
url(r'^workout/stravaimport/(?P\d+)/$',views.workout_getstravaworkout_view),
url(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view),
url(r'^workout/sporttracksimport/(?P\d+)/$',views.workout_getsporttracksworkout_view),
+ url(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all),
url(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view),
url(r'^workout/runkeeperimport/(?P\d+)/$',views.workout_getrunkeeperworkout_view),
url(r'^workout/underarmourimport/$',views.workout_underarmourimport_view),
diff --git a/rowers/utils.py b/rowers/utils.py
index b700cba3..b78310cd 100644
--- a/rowers/utils.py
+++ b/rowers/utils.py
@@ -4,6 +4,21 @@ import numpy as np
lbstoN = 4.44822
+def uniqify(seq, idfun=None):
+ # order preserving
+ if idfun is None:
+ def idfun(x): return x
+ seen = {}
+ result = []
+ for item in seq:
+ marker = idfun(item)
+ # in old Python versions:
+ # if seen.has_key(marker)
+ # but in new ones:
+ if marker in seen: continue
+ seen[marker] = 1
+ result.append(item)
+ return result
def serialize_list(value,token=','):
assert(isinstance(value, list) or isinstance(value, tuple) or isinstance(value,np.ndarray))
diff --git a/rowers/views.py b/rowers/views.py
index 376fa0d1..460ef645 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -267,7 +267,7 @@ def splitstdata(lijst):
return [np.array(t),np.array(latlong)]
-from utils import geo_distance,serialize_list,deserialize_list
+from utils import geo_distance,serialize_list,deserialize_list,uniqify
from rowers.models import checkworkoutuser
# Check if a user is a Coach member
@@ -5764,15 +5764,25 @@ def workout_sporttracksimport_view(request,message=""):
return HttpResponseRedirect(url)
else:
workouts = []
+ r = Rower.objects.get(user=request.user)
+ stids = [int(getidfromsturi(item['uri'])) for item in res.json()['items']]
+ knownstids = uniqify([
+ w.uploadedtosporttracks for w in Workout.objects.filter(user=r)
+ ])
+ newids = [stid for stid in stids if not stid in knownstids]
for item in res.json()['items']:
d = int(float(item['total_distance']))
- i = getidfromsturi(item['uri'])
+ i = int(getidfromsturi(item['uri']))
+ if i in knownstids:
+ nnn = ''
+ else:
+ nnn = 'NEW'
n = item['name']
ttot = str(datetime.timedelta(seconds=int(float(item['duration']))))
s = item['start_time']
r = item['type']
- keys = ['id','distance','duration','starttime','type','name']
- values = [i,d,ttot,s,r,n]
+ keys = ['id','distance','duration','starttime','type','name','new']
+ values = [i,d,ttot,s,r,n,nnn]
res = dict(zip(keys,values))
workouts.append(res)
return render(request,'sporttracks_list_import.html',
@@ -5823,7 +5833,58 @@ def c2listdebug_view(request,message=""):
{'workouts':workouts,
'teams':get_my_teams(request.user),
})
-
+
+# Import all unknown workouts available on Concept2 logbook
+@login_required()
+def workout_getc2workout_all(request,message=""):
+ try:
+ thetoken = c2_open(request.user)
+ except C2NoTokenError:
+ return HttpResponseRedirect("/rowers/me/c2authorize/")
+
+ res = c2stuff.get_c2_workout_list(request.user)
+
+ if (res.status_code != 200):
+ message = "Something went wrong in workout_c2import_view (C2 token refresh)"
+ messages.error(request,message)
+ else:
+ r = Rower.objects.get(user=request.user)
+ c2ids = [item['id'] for item in res.json()['data']]
+ knownc2ids = uniqify([
+ w.uploadedtoc2 for w in Workout.objects.filter(user=r)
+ ])
+ newids = [c2id for c2id in c2ids if not c2id in knownc2ids]
+ for c2id in newids:
+ res = c2stuff.get_c2_workout(request.user,c2id)
+ if (res.status_code == 200):
+ data = res.json()['data']
+ splitdata = None
+ if 'workout' in data:
+ if 'splits' in data['workout']:
+ splitdata = data['workout']['splits']
+ if 'intervals' in data['workout']:
+ splitdata = data['workout']['intervals']
+
+ # Check if workout has stroke data, and get the stroke data
+ if data['stroke_data']:
+ res2 = c2stuff.get_c2_workout_strokes(request.user,c2id)
+ # We have stroke data
+ if res2.status_code == 200:
+ strokedata = pd.DataFrame.from_dict(res2.json()['data'])
+ # create the workout
+ id,message = add_workout_from_strokedata(
+ request.user,c2id,data,strokedata,
+ source='c2')
+ w = Workout.objects.get(id=id)
+ w.uploadedtoc2=c2id
+ w.save()
+ if message:
+ messages.error(request,message)
+
+ url = reverse(workouts_view)
+ return HttpResponseRedirect(url)
+
+
# List of workouts available on Concept2 logbook - for import
@login_required()
def workout_c2import_view(request,message=""):
@@ -5844,6 +5905,12 @@ def workout_c2import_view(request,message=""):
return HttpResponseRedirect(url)
else:
workouts = []
+ r = Rower.objects.get(user=request.user)
+ c2ids = [item['id'] for item in res.json()['data']]
+ knownc2ids = uniqify([
+ w.uploadedtoc2 for w in Workout.objects.filter(user=r)
+ ])
+ newids = [c2id for c2id in c2ids if not c2id in knownc2ids]
for item in res.json()['data']:
d = item['distance']
i = item['id']
@@ -5852,8 +5919,12 @@ def workout_c2import_view(request,message=""):
r = item['type']
s2 = item['source']
c = item['comments']
- keys = ['id','distance','duration','starttime','rowtype','source','comment']
- values = [i,d,ttot,s,r,s2,c]
+ if i in knownc2ids:
+ nnn = ''
+ else:
+ nnn = 'NEW'
+ keys = ['id','distance','duration','starttime','rowtype','source','comment','new']
+ values = [i,d,ttot,s,r,s2,c,nnn]
res = dict(zip(keys,values))
workouts.append(res)
@@ -5959,6 +6030,38 @@ def workout_getsporttracksworkout_view(request,sporttracksid):
})
return HttpResponseRedirect(url)
+# Imports all new workouts from SportTracks
+@login_required()
+def workout_getsporttracksworkout_all(request):
+ res = sporttracksstuff.get_sporttracks_workout_list(request.user)
+ if (res.status_code == 200):
+ r = Rower.objects.get(user=request.user)
+ stids = [int(getidfromsturi(item['uri'])) for item in res.json()['items']]
+ knownstids = uniqify([
+ w.uploadedtosporttracks for w in Workout.objects.filter(user=r)
+ ])
+ newids = [stid for stid in stids if not stid in knownstids]
+ for sporttracksid in newids:
+ res = sporttracksstuff.get_sporttracks_workout(
+ request.user,sporttracksid)
+ data = res.json()
+
+ id,message = add_workout_from_stdata(request.user,sporttracksid,data)
+ print sporttracksid,id
+ if id==0:
+ messages.error(request,message)
+
+ else:
+ w = Workout.objects.get(id=id)
+ w.uploadedtosporttracks=sporttracksid
+ print 'saved'
+ w.save()
+
+ url = reverse(workouts_view)
+ return HttpResponseRedirect(url)
+
+
+
# Imports a workout from Concept2
@login_required()
def workout_getc2workout_view(request,c2id):
From be13626fbc854b6cd28331fed5c6917873a73bae Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 17 May 2017 13:34:01 +0200
Subject: [PATCH 06/10] strava done
---
rowers/templates/strava_list_import.html | 86 ++++++++++++------------
rowers/urls.py | 1 +
rowers/views.py | 62 +++++++++++++++--
3 files changed, 102 insertions(+), 47 deletions(-)
diff --git a/rowers/templates/strava_list_import.html b/rowers/templates/strava_list_import.html
index a99ea834..b384cd5c 100644
--- a/rowers/templates/strava_list_import.html
+++ b/rowers/templates/strava_list_import.html
@@ -6,47 +6,47 @@
{% block content %}
Available on Strava
- {% if data %}
-
-
-
- | Import |
- Duration |
- Type |
- Date |
- Distance |
- Name |
-
-
-
- {% for workout in data %}
-
- {% for key,value in workout.items %}
- {% if key == "start_date" %}
- | {{ value }} |
- {% endif %}
- {% if key == "name" %}
- {{ value }} |
- {% endif %}
- {% if key == "type" %}
- {{ value }} |
- {% endif %}
- {% if key == "distance" %}
- {{ value }}m |
- {% endif %}
- {% if key == "elapsed_time" %}
- {{ value }} |
- {% endif %}
- {% if key == "id" %}
- Import |
- {% endif %}
- {% endfor %}
-
-
- {% endfor %}
-
-
- {% else %}
- No workouts found
- {% endif %}
+{% if workouts %}
+
+
+
This imports all workouts that have not been imported to rowsandall.com.
+ The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout.
+
+
+
+
+
+
+
+ | Import |
+ Name |
+ Date |
+ Duration |
+ Distance |
+ Type |
+ New |
+
+
+
+ {% for workout in workouts %}
+
+ |
+ Import |
+ {{ workout|lookup:'name' }} |
+ {{ workout|lookup:'starttime' }} |
+ {{ workout|lookup:'duration' }} |
+ {{ workout|lookup:'distance' }} m |
+ {{ workout|lookup:'type' }} |
+ {{ workout|lookup:'new' }} |
+
+
+ {% endfor %}
+
+
+
+{% else %}
+ No workouts found
+{% endif %}
{% endblock %}
diff --git a/rowers/urls.py b/rowers/urls.py
index ec5d73a4..b9848e61 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -213,6 +213,7 @@ urlpatterns = [
url(r'^workout/c2import/(?P\d+)/$',views.workout_getc2workout_view),
url(r'^workout/c2import/all/$',views.workout_getc2workout_all),
url(r'^workout/stravaimport/(?P\d+)/$',views.workout_getstravaworkout_view),
+ url(r'^workout/stravaimport/all/$',views.workout_getstravaworkout_all),
url(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view),
url(r'^workout/sporttracksimport/(?P\d+)/$',views.workout_getsporttracksworkout_view),
url(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all),
diff --git a/rowers/views.py b/rowers/views.py
index 460ef645..685e69d8 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -5651,10 +5651,33 @@ def workout_stravaimport_view(request,message=""):
url = reverse(workouts_view)
return HttpResponseRedirect(url)
else:
- data = res.json()
+ workouts = []
+ r = Rower.objects.get(user=request.user)
+ stravaids = [int(item['id']) for item in res.json()]
+ knownstravaids = uniqify([
+ w.uploadedtostrava for w in Workout.objects.filter(user=r)
+ ])
+ newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
+
+ for item in res.json():
+ d = int(float(item['distance']))
+ i = item['id']
+ if i in knownstravaids:
+ nnn = ''
+ else:
+ nnn = 'NEW'
+ n = item['name']
+ ttot = str(datetime.timedelta(seconds=int(float(item['elapsed_time']))))
+ s = item['start_date']
+ r = item['type']
+ keys = ['id','distance','duration','starttime','type','name','new']
+ values = [i,d,ttot,s,r,n,nnn]
+ res = dict(zip(keys,values))
+ workouts.append(res)
+
return render(request,'strava_list_import.html',
- {'data':data,
+ {'workouts':workouts,
'teams':get_my_teams(request.user),
})
@@ -6047,14 +6070,45 @@ def workout_getsporttracksworkout_all(request):
data = res.json()
id,message = add_workout_from_stdata(request.user,sporttracksid,data)
- print sporttracksid,id
if id==0:
messages.error(request,message)
else:
w = Workout.objects.get(id=id)
w.uploadedtosporttracks=sporttracksid
- print 'saved'
+ w.save()
+
+ url = reverse(workouts_view)
+ return HttpResponseRedirect(url)
+
+
+# Imports all new workouts from SportTracks
+@login_required()
+def workout_getstravaworkout_all(request):
+ res = stravastuff.get_strava_workout_list(request.user)
+ if (res.status_code == 200):
+ r = Rower.objects.get(user=request.user)
+ stravaids = [int(item['id']) for item in res.json()]
+ knownstravaids = uniqify([
+ w.uploadedtostrava for w in Workout.objects.filter(user=r)
+ ])
+ newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
+
+ for stravaid in newids:
+ res = stravastuff.get_strava_workout(request.user,stravaid)
+ strokedata = res[1]
+ data = res[0]
+
+ id,message = add_workout_from_strokedata(request.user,stravaid,data,strokedata,
+ source='strava',
+ workoutsource='strava')
+
+ if id==0:
+ messages.error(request,message)
+
+ else:
+ w = Workout.objects.get(id=id)
+ w.uploadedtostrava=stravaid
w.save()
url = reverse(workouts_view)
From ba38a0747a013397cd92793815cfc2cae91a2e3c Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 17 May 2017 16:30:05 +0200
Subject: [PATCH 07/10] csv to admin functionality
---
rowers/urls.py | 1 +
rowers/views.py | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/rowers/urls.py b/rowers/urls.py
index b9848e61..33f953f7 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -170,6 +170,7 @@ urlpatterns = [
url(r'^workout/(?P\d+)/comment$',views.workout_comment_view),
url(r'^workout/(?P\d+)/emailtcx$',views.workout_tcxemail_view),
url(r'^workout/(?P\d+)/emailcsv$',views.workout_csvemail_view),
+ url(r'^workout/(?P\d+)/csvtoadmin$',views.workout_csvtoadmin_view),
url(r'^workout/compare/(?P\d+)/$',views.workout_comparison_list),
url(r'^workout/compare2/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P\w+.*)/$',views.workout_comparison_view),
url(r'^workout/compare/(?P\d+)/(?P\d+-\d+-\d+)/(?P\w+.*)$',views.workout_comparison_list),
diff --git a/rowers/views.py b/rowers/views.py
index 685e69d8..7a48b6ae 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -1150,6 +1150,41 @@ def workout_csvemail_view(request,id=0):
return response
+# Get Workout CSV file and send it to user's email address
+@login_required()
+def workout_csvtoadmin_view(request,id=0):
+ message = ""
+ r = Rower.objects.get(user=request.user)
+ try:
+ w = Workout.objects.get(id=id)
+ except Workout.DoesNotExist:
+ raise Http404("Workout doesn't exist")
+
+ csvfile = w.csvfilename
+ if settings.DEBUG:
+ res = handle_sendemailcsv.delay(
+ 'Sander',
+ 'Roosendaal',
+ 'roosendaalsander@gmail.com',csvfile)
+
+ else:
+ res = queuehigh.enqueue(
+ handle_sendemailcsv,
+ 'Sander',
+ 'Roosendaal',
+ 'roosendaalsander@gmail.com',
+ csvfile)
+
+ successmessage = "The CSV file was sent to the site admin per email"
+ messages.info(request,successmessage)
+ url = reverse(workout_export_view,
+ kwargs = {
+ 'id':str(w.id),
+ })
+ response = HttpResponseRedirect(url)
+
+ return response
+
# Send workout to TP
@login_required()
def workout_tp_upload_view(request,id=0):
From 9578be77c0715a84bad8807dc0f1120bacaf8971 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 18 May 2017 10:19:39 +0200
Subject: [PATCH 08/10] some small improvement (export messages)
---
rowers/views.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/rowers/views.py b/rowers/views.py
index 7a48b6ae..1b421e1f 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -1345,8 +1345,10 @@ def workout_c2_upload_view(request,id=0):
except C2NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/")
- if message:
+ if message and c2id <=0:
messages.error(request,message)
+ elif message:
+ messages.info(request,message)
url = reverse(workout_export_view,
@@ -1534,6 +1536,8 @@ def workout_sporttracks_upload_view(request,id=0):
sporttracksid = sporttracksstuff.getidfromresponse(response)
w.uploadedtosporttracks = sporttracksid
w.save()
+ message = "Upload to SportTracks was successful"
+ messages.info(message)
url = "/rowers/workout/"+str(w.id)+"/export"
return HttpResponseRedirect(url)
else:
From 40313f9680469af96276bac19754c2f71c4197d9 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 18 May 2017 15:23:09 +0200
Subject: [PATCH 09/10] adding OTW power slack
---
rowers/models.py | 5 +-
rowers/templates/rower_form.html | 12 ++++-
rowers/views.py | 88 +++++++++++++++++++++++++-------
3 files changed, 83 insertions(+), 22 deletions(-)
diff --git a/rowers/models.py b/rowers/models.py
index fa0c839d..15046a7a 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -173,13 +173,14 @@ class Rower(models.Model):
# Power Zone Data
ftp = models.IntegerField(default=226,verbose_name="Functional Threshold Power")
+ otwslack = models.IntegerField(default=15,verbose_name="OTW Power slack")
pw_ut2 = models.IntegerField(default=124,verbose_name="UT2 Power")
pw_ut1 = models.IntegerField(default=171,verbose_name="UT1 Power")
pw_at = models.IntegerField(default=203,verbose_name="AT Power")
pw_tr = models.IntegerField(default=237,verbose_name="TR Power")
pw_an = models.IntegerField(default=273,verbose_name="AN Power")
-
+
powerzones = PowerZonesField(default=['Rest',
'Pwr UT2',
'Pwr UT1',
@@ -576,7 +577,7 @@ class AdvancedWorkoutForm(ModelForm):
class RowerPowerForm(ModelForm):
class Meta:
model = Rower
- fields = ['ftp']
+ fields = ['ftp','otwslack']
# Form to set rower's Power zones, including test routines
# to enable consistency
diff --git a/rowers/templates/rower_form.html b/rowers/templates/rower_form.html
index c495cb23..9fc7b380 100644
--- a/rowers/templates/rower_form.html
+++ b/rowers/templates/rower_form.html
@@ -7,6 +7,7 @@
Heart Rate Zones
+
Set your heart rate zones with this form.
{% if form.errors %}
Please correct the error{{ form.errors|pluralize }} below.
@@ -27,6 +28,8 @@
Power Zones
+
The power zones are defined relative to power as measured by the
+ indoor rower.
-
Functional Threshold Power
+
Functional Threshold Power and OTW Slack
Use this form to quickly change your zones based on the power of a
recent
- full out 60 minutes effort. It will update all zones defined above.
+ full out 60 minutes effort on the ergometer.
+ It will update all zones defined above.
+
The OTW Power Slack is the percentage drop of your On-the-water
+ rowing power
+ vs the erg power. Typical values are around 15%. This will lower
+ the power zones for your OTW workouts.