diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py
index 4a082f35..432b5634 100644
--- a/rowers/c2stuff.py
+++ b/rowers/c2stuff.py
@@ -14,6 +14,7 @@ import datetime
from requests import Request, Session
import rowers.mytypes as mytypes
from rowers.mytypes import otwtypes
+from rowers.rower_rules import is_workout_user
from iso8601 import ParseError
import numpy
@@ -863,7 +864,7 @@ def workout_c2_upload(user,w):
r = Rower.objects.get(user=user)
# ready to upload. Hurray
- if (checkworkoutuser(user,w)):
+ if (is_workout_user(user,w)):
c2userid = get_userid(r.c2token)
if not c2userid:
raise NoTokenError("User has no token")
diff --git a/rowers/imports.py b/rowers/imports.py
index fe1a85e6..d3a3ab01 100644
--- a/rowers/imports.py
+++ b/rowers/imports.py
@@ -43,7 +43,7 @@ from django.contrib.auth.decorators import login_required
# from .models import Profile
from rowingdata import rowingdata, make_cumvalues
import pandas as pd
-from rowers.models import Rower,Workout,checkworkoutuser,TombStone
+from rowers.models import Rower,Workout,TombStone
import rowers.mytypes as mytypes
from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
@@ -54,7 +54,7 @@ from rowsandall_app.settings import (
from rowers.utils import (
NoTokenError, custom_exception_handler, ewmovingaverage,
- geo_distance,isprorower,uniqify
+ geo_distance,uniqify
)
@@ -144,7 +144,7 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''):
else:
grant_type = post_data.pop('grant_type',None)
- if oauth_data['bearer_auth']:
+ if oauth_data['bearer_auth']:
headers['authorization'] = 'Bearer %s' % access_token
baseurl = oauth_data['base_url']
@@ -159,7 +159,7 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''):
headers=headers)
-
+
if response.status_code == 200 or response.status_code == 201:
token_json = response.json()
else:
@@ -186,7 +186,7 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''):
expires_in = int(expires_in)
except (TypeError,ValueError):
expires_in = 0
-
+
return [thetoken,expires_in,refresh_token]
@@ -199,20 +199,20 @@ def imports_get_token(
client_secret = oauth_data['client_secret']
client_id = oauth_data['client_id']
base_uri = oauth_data['base_url']
-
+
client_auth = requests.auth.HTTPBasicAuth(
client_id,client_secret
)
-
+
post_data = {"grant_type": "authorization_code",
"code": code,
"redirect_uri": redirect_uri,
"client_secret": client_secret,
"client_id": client_id,
}
-
+
try:
headers = oauth_data['headers']
except KeyError:
@@ -229,7 +229,7 @@ def imports_get_token(
else:
grant_type = post_data.pop('grant_type',None)
-
+
if 'json' in oauth_data['content_type']:
response = requests.post(
base_uri,
@@ -262,7 +262,7 @@ def imports_get_token(
else:
return [0,0,0]
-
+
return [thetoken,expires_in,refresh_token]
# Make authorization URL including random string
@@ -293,8 +293,8 @@ def imports_token_refresh(user,tokenname,refreshtokenname,expirydatename,oauth_d
# for Strava transition
if not refreshtoken:
refreshtoken = getattr(r,tokenname)
-
-
+
+
res = imports_do_refresh_token(refreshtoken,oauth_data)
access_token = res[0]
expires_in = res[1]
@@ -309,4 +309,3 @@ def imports_token_refresh(user,tokenname,refreshtokenname,expirydatename,oauth_d
r.save()
return access_token
-
diff --git a/rowers/models.py b/rowers/models.py
index 593dfd1e..c0c9c520 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -666,6 +666,9 @@ class CoachingGroup(models.Model):
rs = Rower.objects.filter(coachinggroups__in=[self])
return rs.count()
+ def get_coaches(self):
+ return Rower.objects.filter(mycoachgroup=self)
+
# Extension of User with rowing specific data
@python_2_unicode_compatible
class Rower(models.Model):
@@ -1173,95 +1176,7 @@ class BasePlannedSessionFormSet(BaseFormSet):
return
-# Check if workout is owned by this user
-def checkworkoutuser(user,workout):
- if user.is_anonymous:
- return False
- try:
- r = Rower.objects.get(user=user)
- if workout.user == r:
- return True
- coaches = []
- for group in workout.user.coachinggroups.all():
- coach = Rower.objects.get(mycoachgroup=group)
- coaches.append(coach)
- for coach in coaches:
- if user.rower == coach and workout.privacy == 'visible':
- return True
- else:
- return False
- except Rower.DoesNotExist:
- return False
-# Check if workout may be viewed by this user
-def checkworkoutuserview(user,workout):
- if user.is_anonymous:
- return False
- try:
- r = Rower.objects.get(user=user)
- if workout.user == r:
- return True
- teams = workout.user.team.all()
-
- for team in teams:
- if team in r.team.all():
- return True
- return False
- except Rower.DoesNotExist:
- return False
-
- return False
-
-def checkviewworkouts(user,rower):
- try:
- r = user.rower
- if rower == r:
- return True
- teams = Team.objects.filter(manager=user)
-
- if rower in Rower.objects.filter(team__in=teams):
- return True
-
- if rower in Rower.objects.filter(coachinggroups__in=[user.rower.mycoachgroup]):
- return True
-
-
- except Rower.DoesNotExist:
- return False
-
-# check if user is plan and rower is in his group
-def checkaccessplanuser(user,rower):
- try:
- r = Rower.objects.get(user=user)
- if rower == r:
- return True
- team_managers = [t.manager for t in rower.team.all() if t.manager.rower.rowerplan in ['plan','coach','freecoach']]
- if user.rower.rowerplan != 'basic':
- return user in team_managers
- else:
- return False
-
- return False
- except Rower.DoesNotExist:
- return False
-
-# Check if user is coach or rower
-def checkaccessuser(user,rower):
- try:
- r = Rower.objects.get(user=user)
- if rower == r:
- return True
- coaches = []
- for group in rower.coachinggroups.all():
- coach = Rower.objects.get(mycoachgroup=group)
- coaches.append(coach)
- for coach in coaches:
- if user.rower == coach:
- return True
- else:
- return False
- except Rower.DoesNotExist:
- return False
timezones = (
(x,x) for x in pytz.common_timezones
diff --git a/rowers/polarstuff.py b/rowers/polarstuff.py
index dc4f397a..2466c1e7 100644
--- a/rowers/polarstuff.py
+++ b/rowers/polarstuff.py
@@ -38,7 +38,7 @@ from django.contrib.auth.decorators import login_required
from rowingdata import rowingdata
import pandas as pd
from rowers.models import Rower,Workout
-from rowers.models import checkworkoutuser
+
import rowers.dataprep as dataprep
from rowers.dataprep import columndict
@@ -50,7 +50,7 @@ from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
from django_mailbox.models import Message,Mailbox,MessageAttachment
from rowsandall_app.settings import (
- POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,
+ POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,
)
#baseurl = 'https://polaraccesslink.com/v3-example'
@@ -79,7 +79,7 @@ def get_token(code):
except TypeError:
headers = { 'Authorization': 'Basic %s' % base64.b64encode(
bytes(auth_string,'utf-8')).decode('utf-8') }
-
+
response = requests.post("https://polarremote.com/v2/oauth2/token",
data=post_data,
headers=headers)
@@ -110,7 +110,7 @@ def make_authorization_url():
import urllib
url = "https://flow.polar.com/oauth2/authorization" +urllib.parse.urlencode(params)
-
+
return HttpResponseRedirect(url)
def get_polar_notifications():
@@ -130,13 +130,13 @@ def get_polar_notifications():
response = requests.get(url, headers=headers)
available_data = []
-
+
if response.status_code == 200:
available_data = response.json()['available-user-data']
-
+
return available_data
-from rowers.utils import isprorower
+from rowers.rower_rules import ispromember
def get_all_new_workouts(available_data,testing=False):
for record in available_data:
@@ -146,7 +146,7 @@ def get_all_new_workouts(available_data,testing=False):
try:
r = Rower.objects.get(polaruserid=record['user-id'])
u = r.user
- if r.polar_auto_import and isprorower(r):
+ if r.polar_auto_import and ispromember(u):
exercise_list = get_polar_workouts(u)
if testing:
print(exercise_list)
@@ -154,13 +154,13 @@ def get_all_new_workouts(available_data,testing=False):
pass
return 1
-
+
def get_polar_workouts(user):
r = Rower.objects.get(user=user)
exercise_list = []
-
+
if (r.polartoken == '') or (r.polartoken is None):
s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s)
@@ -195,7 +195,7 @@ def get_polar_workouts(user):
uploadoptions,
default_flow_style=False
)
-
+
transactionid = response.json()['transaction-id']
url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}'.format(
transactionid = transactionid,
@@ -229,11 +229,11 @@ def get_polar_workouts(user):
a = MessageAttachment(message=msg,document=filename[6:])
a.save()
-
+
exercise_dict['filename'] = filename
else:
exercise_dict['filename'] = ''
-
+
exercise_list.append(exercise_dict)
# commit transaction
@@ -280,7 +280,7 @@ def get_polar_user_info(user,physical=False):
def get_polar_workout(user,id,transactionid):
-
+
r = Rower.objects.get(user=user)
if (r.polartoken == '') or (r.polartoken is None):
s = "Token doesn't exist. Need to authorize"
@@ -326,7 +326,7 @@ def get_polar_workout(user,id,transactionid):
)
response = requests.get(url,headers = headers2)
-
+
if response.status_code == 200:
result = response.text
# commit transaction
@@ -337,7 +337,3 @@ def get_polar_workout(user,id,transactionid):
return result
return None
-
-
-
-
diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py
index 386cb5eb..258c86b3 100644
--- a/rowers/rower_rules.py
+++ b/rowers/rower_rules.py
@@ -19,6 +19,154 @@ def user_is_not_basic(user):
def is_coach(user):
return user.rower.rowerplan in ['coach','freecoach']
+@rules.predicate
+def is_promember(user):
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ return r.rowerplan in ['pro','coach','plan']
+
+@rules.predicate
+def is_protrial(user):
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ if r.rowerplan == 'basic':
+ return r.protrialexpires >= datetime.date.today()
+ if r.rowerplan == 'freecoach':
+ if r.mycoachgroup is not None:
+ return len(r.mycoachgroup)>=4
+
+ return False
+
+ispromember = is_promember | is_protrial
+
+# User / Coach relationships (Rower object)
+
+@rules.predicate
+def can_plan(user):
+ return user.rower.rowerplan in ['plan','coach','freecoach']
+
+@rules.predicate
+def is_coach_user(user,rower):
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ if rower == r:
+ return True
+
+ coaches = []
+
+ for group in r.coachinggroups.all():
+ newcoaches = group.get_coaches()
+ for coach in newcoaches:
+ coaches.append(coach)
+ print(coaches)
+ for coach in coaches:
+ if rower == coach:
+ return True
+
+ return False
+
+@rules.predicate
+def is_rower_team_member(user,rower):
+ if user.rower == rower:
+ return True
+
+ teams = user.rower.team.all()
+
+ for team in teams:
+ if team.private == 'open':
+ if team in rower.team.all():
+ return True
+ if team.manager == rower.user:
+ return True
+
+ return False
+
+@rules.predicate
+def can_plan_user(user,rower):
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ if rower == r:
+ return True
+
+ # below
+ team_managers = [t.manager for t in rower.team.all() and can_plan(t.manager)]
+ if user_is_not_basic(user):
+ return user in team_managers
+
+ return False
+
+rules.add_perm('rower.can_plan',can_plan_user) # replaces checkaccessplanuser
+rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser
+
+
+# WORKOUT permissions
+
+@rules.predicate
+def is_workout_user(user,workout):
+ if user.is_anonymous:
+ return False
+
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ if workout.user == r:
+ return True
+
+ coaches = []
+ for group in workout.user.coachinggroups.all():
+ coach = group.coachingrole
+ coaches.append(coach)
+ for coach in coaches:
+ if r == coach and workout.privacy == 'visible':
+ return True
+
+ return False
+
+@rules.predicate
+def can_view_workout(user,workout):
+ if user.is_anonymous:
+ if workout.privacy != 'private':
+ return True
+ return False
+
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ teams = workout.user.team.all()
+
+ for team in teams:
+ if team in r.team.all():
+ return True
+
+ return False
+
+rules.add_perm('workout.change_workout',is_workout_user) # replaces checkworkoutuser
+rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutuserview
+
+
+
+# checkviewworkouts
+
+# PLANNING permissions
+
+# checkaccessplanuser (models.py)
+
# TEAM permissions
@rules.predicate
diff --git a/rowers/runkeeperstuff.py b/rowers/runkeeperstuff.py
index 5e21b11a..4b2b3322 100644
--- a/rowers/runkeeperstuff.py
+++ b/rowers/runkeeperstuff.py
@@ -7,6 +7,8 @@ from __future__ import unicode_literals, absolute_import
from rowers.imports import *
import re
+from rowers.rower_rules import is_workout_user
+
from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
@@ -285,7 +287,7 @@ def workout_runkeeper_upload(user,w):
# ready to upload. Hurray
- if (checkworkoutuser(user,w)):
+ if (is_workout_user(user,w)):
data = createrunkeeperworkoutdata(w)
if not data:
message = "Data error in Runkeeper Upload"
diff --git a/rowers/sporttracksstuff.py b/rowers/sporttracksstuff.py
index 2828a18f..28fc3168 100644
--- a/rowers/sporttracksstuff.py
+++ b/rowers/sporttracksstuff.py
@@ -18,6 +18,7 @@ from rowsandall_app.settings import (
)
import rowers.mytypes as mytypes
+from rowers.rower_rules import is_workout_user
oauth_data = {
'client_id': SPORTTRACKS_CLIENT_ID,
@@ -265,7 +266,7 @@ def workout_sporttracks_upload(user,w):
thetoken = sporttracks_open(user)
- if (checkworkoutuser(user,w)):
+ if (is_workout_user(user,w)):
data = createsporttracksworkoutdata(w)
if not data:
message = "Data error"
diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py
index d8663c97..37e33dbc 100644
--- a/rowers/stravastuff.py
+++ b/rowers/stravastuff.py
@@ -18,6 +18,8 @@ queuehigh = django_rq.get_queue('low')
from rowers.dataprep import columndict
+from rowers.rower_rules import is_workout_user
+
import stravalib
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
@@ -632,7 +634,7 @@ def workout_strava_upload(user,w):
s = "Token doesn't exist. Need to authorize"
raise NoTokenError("Your hovercraft is full of eels")
else:
- if (checkworkoutuser(user,w)):
+ if (is_workout_user(user,w)):
try:
tcxfile,tcxmesg = createstravaworkoutdata(w)
if tcxfile:
diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py
index b3b7b5d5..cb0ca2d2 100644
--- a/rowers/templatetags/rowerfilters.py
+++ b/rowers/templatetags/rowerfilters.py
@@ -21,7 +21,7 @@ from rowers.plannedsessions import (
from rowers import c2stuff, runkeeperstuff
from rowers.c2stuff import c2_open
from rowers.runkeeperstuff import runkeeper_open
-from rowers.models import checkaccessuser
+from rowers.rower_rules import is_coach_user, is_workout_user
from rowers.mytypes import otwtypes
from rowers.utils import NoTokenError
@@ -385,14 +385,13 @@ def is_session_manager(id,user):
return ps.manager == user
-from rowers.models import checkworkoutuser
@register.filter
def may_edit(workout,request):
mayedit = 0
if request.user == workout.user.user:
mayedit = True
- if checkworkoutuser(request.user,workout):
+ if is_workout_user(request.user,workout):
mayedit = True
return mayedit
@@ -413,7 +412,7 @@ def mayeditplan(obj,request):
return request.user == obj.manager.user
rr = Rower.objects.get(user=request.user)
- if checkaccessuser(request.user,obj.rower) and rr.rowerplan not in ['basic','pro']:
+ if is_coach_user(request.user,obj.rower) and rr.rowerplan not in ['basic','pro']:
mayedit = True
diff --git a/rowers/tests/mocks.py b/rowers/tests/mocks.py
index 278c1b03..7acce340 100644
--- a/rowers/tests/mocks.py
+++ b/rowers/tests/mocks.py
@@ -18,7 +18,7 @@ from django.test import TestCase, Client,override_settings
from django.core.management import call_command
from django.utils.six import StringIO
from django.test.client import RequestFactory
-from rowers.views import checkworkoutuser,c2_open
+from rowers.views import c2_open
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage
from rowers.forms import DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail
import rowers.plots as plots
diff --git a/rowers/tests/statements.py b/rowers/tests/statements.py
index 0276fe67..4b4e50a4 100644
--- a/rowers/tests/statements.py
+++ b/rowers/tests/statements.py
@@ -7,12 +7,12 @@ try:
FileNotFoundError
except NameError:
FileNotFoundError = None
-
+
try:
OSError
except NameError:
OSError = None
-
+
import pytest
pytestmark = pytest.mark.django_db
@@ -26,7 +26,7 @@ from django.core.management import call_command
from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils.six import StringIO
from django.test.client import RequestFactory
-from rowers.views import checkworkoutuser,c2_open, multi_compare_view
+from rowers.views import c2_open, multi_compare_view
from rowers.forms import (
DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail,
@@ -101,22 +101,22 @@ def get_random_file(filename='rowers/tests/testdata/testdata.csv',name=''):
fromstring = 'test_%s_' % mod.__name__
except AttributeError:
fromstring = 'none_'
-
+
row = rdata(filename)
totaldist = row.df['cum_dist'].max()
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
totaltime = totaltime+row.df.loc[row.df.index[0],' ElapsedTime (sec)']
-
-
+
+
hours = int(totaltime/3600.)
minutes = int((totaltime - 3600.*hours)/60.)
seconds = int(totaltime - 3600.*hours - 60.*minutes)
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
-
+
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
duration = datetime.time(hour=hours,minute=minutes,second=seconds)
-
+
workoutdate = row.rowdatetime.date()
workoutstarttime = row.rowdatetime
@@ -139,7 +139,7 @@ def get_random_file(filename='rowers/tests/testdata/testdata.csv',name=''):
'duration':duration,
'totaldist':totaldist,
}
-
+
return thedict
class UserFactory(factory.DjangoModelFactory):
@@ -168,11 +168,11 @@ class RaceFactory(factory.DjangoModelFactory):
sessiontype = 'indoorrace'
sessionvalue = 1
sessionmode = 'time'
-
+
class WorkoutFactory(factory.DjangoModelFactory):
class Meta:
model = Workout
-
+
name = factory.LazyAttribute(lambda _: faker.word())
notes = faker.text()
startdatetime = get_random_file(name=faker.word())['startdatetime']
@@ -220,5 +220,3 @@ def cleanup(request):
request.addfinalizer(remove_test_files)
-
-
diff --git a/rowers/tests/test_urls.py b/rowers/tests/test_urls.py
index 1f2eeee2..e3be6bac 100644
--- a/rowers/tests/test_urls.py
+++ b/rowers/tests/test_urls.py
@@ -209,7 +209,6 @@ class URLTests(TestCase):
'/rowers/workout/'+encoded1+'/editintervals/',
'/rowers/workout/'+encoded1+'/flexchart/',
'/rowers/workout/'+encoded1+'/forcecurve/',
- '/rowers/workout/'+encoded1+'/get-testscript/',
'/rowers/workout/'+encoded1+'/get-thumbnails/',
'/rowers/workout/'+encoded1+'/histo/',
'/rowers/workout/'+encoded1+'/image/',
diff --git a/rowers/tpstuff.py b/rowers/tpstuff.py
index efdf9e8f..010e9f59 100644
--- a/rowers/tpstuff.py
+++ b/rowers/tpstuff.py
@@ -42,6 +42,8 @@ oauth_data = {
'scope':'write',
}
+from rowers.rower_rules import is_workout_user
+
# Checks if user has UnderArmour token, renews them if they are expired
def tp_open(user):
@@ -171,7 +173,7 @@ def workout_tp_upload(user,w):
# need some code if token doesn't refresh
- if (checkworkoutuser(user,w)):
+ if (is_workout_user(user,w)):
tcxfile = createtpworkoutdata(w)
if tcxfile:
res,reason,status_code,headers = uploadactivity(
diff --git a/rowers/traverselinktest.py b/rowers/traverselinktest.py
index 546a5ccf..3856fb44 100644
--- a/rowers/traverselinktest.py
+++ b/rowers/traverselinktest.py
@@ -9,7 +9,7 @@ from django.test import TestCase, Client,override_settings
from django.core.management import call_command
from django.utils.six import StringIO
from django.test.client import RequestFactory
-from .views import checkworkoutuser,c2_open
+from .views import c2_open
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage
from rowers.forms import DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail
import rowers.plots as plots
@@ -55,12 +55,12 @@ class TraverseLinksTest(TestCase):
name='testworkout',workouttype='On-water',
user=self.r,date=nu.strftime('%Y-%m-%d'),
starttime=nu.strftime('%H:%M:%S'),
- duration="0:55:00",distance=8000)
+ duration="0:55:00",distance=8000)
self.w2 = Workout.objects.create(
name='testworkout 2',workouttype='On-water',
user=self.r,date=nu.strftime('%Y-%m-%d'),
starttime=nu.strftime('%H:%M:%S'),
- duration="0:55:00",distance=8000)
+ duration="0:55:00",distance=8000)
if self.client.login(
username="superuser1", password="pwd"):
if VERBOSE:
diff --git a/rowers/underarmourstuff.py b/rowers/underarmourstuff.py
index 2d18a21f..7d68d2c3 100644
--- a/rowers/underarmourstuff.py
+++ b/rowers/underarmourstuff.py
@@ -9,6 +9,7 @@ import numpy
import rowers.mytypes as mytypes
from rowers.mytypes import otwtypes
+from rowers.rower_rules import is_workout_user
from rowsandall_app.settings import (
UNDERARMOUR_CLIENT_KEY,
@@ -331,7 +332,7 @@ def workout_ua_upload(user,w):
# ready to upload. Hurray
- if (checkworkoutuser(user,w)):
+ if (is_workout_user(user,w)):
data = createunderarmourworkoutdata(w)
# return HttpResponse(json.dumps(data))
if not data:
diff --git a/rowers/uploads.py b/rowers/uploads.py
index 2d63b557..b0676914 100644
--- a/rowers/uploads.py
+++ b/rowers/uploads.py
@@ -14,6 +14,8 @@ from rowers.tasks import (
from rowers.models import GraphImage
+from rowers.rower_rules import ispromember
+
from PIL import Image
import numpy as np
@@ -472,6 +474,8 @@ import rowers.runkeeperstuff as runkeeperstuff
import rowers.underarmourstuff as underarmourstuff
import rowers.tpstuff as tpstuff
+from rowers.rower_rules import is_promember
+
def set_workouttype(w,options):
try:
w.workouttype = options['workouttype']
@@ -500,8 +504,6 @@ def make_private(w,options):
return 1
-from rowers.utils import isprorower
-
def do_sync(w,options):
try:
if options['stravaid'] != 0:
@@ -511,7 +513,7 @@ def do_sync(w,options):
except KeyError:
pass
- if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and isprorower(w.user)):
+ if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and ispromember(w.user)):
if ('upload_to_C2' in options and not options['upload_to_C2']):
pass
else:
@@ -521,7 +523,7 @@ def do_sync(w,options):
id = 0
message = "Something went wrong with the Concept2 sync"
- if ('upload_to_Strava' in options and options['upload_to_Strava']) or (w.user.strava_auto_export and isprorower(w.user)):
+ if ('upload_to_Strava' in options and options['upload_to_Strava']) or (w.user.strava_auto_export and ispromember(w.user)):
if ('upload_to_Strava' in options and not options['upload_to_Strava']):
pass
else:
@@ -534,7 +536,7 @@ def do_sync(w,options):
message = "Please connect to Strava first"
- if ('upload_to_SportTracks' in options and options['upload_to_SportTracks']) or (w.user.sporttracks_auto_export and isprorower(w.user)):
+ if ('upload_to_SportTracks' in options and options['upload_to_SportTracks']) or (w.user.sporttracks_auto_export and ispromember(w.user)):
if ('upload_to_SportTracks' in options and not options['upload_to_SportTracks']):
pass
else:
@@ -548,7 +550,7 @@ def do_sync(w,options):
id = 0
- if ('upload_to_RunKeeper' in options and options['upload_to_RunKeeper']) or (w.user.runkeeper_auto_export and isprorower(w.user)):
+ if ('upload_to_RunKeeper' in options and options['upload_to_RunKeeper']) or (w.user.runkeeper_auto_export and ispromember(w.user)):
if ('upload_to_RunKeeper' in options and not options['upload_to_RunKeeper']):
pass
else:
@@ -561,7 +563,7 @@ def do_sync(w,options):
message = "Please connect to Runkeeper first"
id = 0
- if ('upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']) or (w.user.mapmyfitness_auto_export and isprorower(w.user)):
+ if ('upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']) or (w.user.mapmyfitness_auto_export and ispromember(w.user)):
if ('upload_to_MapMyFitness' in options and not options['upload_to_MapMyFitness']):
pass
else:
@@ -574,7 +576,7 @@ def do_sync(w,options):
id = 0
- if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export and isprorower(w.user)):
+ if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export and ispromember(w.user)):
if ('upload_to_TrainingPeaks' in options and not options['upload_to_TrainingPeaks']):
pass
else:
diff --git a/rowers/urls.py b/rowers/urls.py
index 6a061af4..15b7768f 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -289,8 +289,6 @@ urlpatterns = [
name='otw_use_gps'),
re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/toggle-ranking/$',views.workout_toggle_ranking,
name='workout_toggle_ranking'),
- re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/get-testscript/$',views.get_testscript,
- name='get_testscript'),
re_path(r'^workout/upload/team/$',views.team_workout_upload_view,name='team_workout_upload_view'),
re_path(r'^workout/upload/$',views.workout_upload_view,name='workout_upload_view'),
re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/histo/$',views.workout_histo_view,
diff --git a/rowers/utils.py b/rowers/utils.py
index d4e47993..a1258b1c 100644
--- a/rowers/utils.py
+++ b/rowers/utils.py
@@ -109,7 +109,7 @@ def get_call():
""" % (call1, call2)
-
+
return call
def absolute(request):
@@ -131,7 +131,7 @@ def trcolors(r1,g1,b1,r2,g2,b2):
h1,s1,v1 = colorsys.rgb_to_hsv(r1,g1,b1)
h2,s2,v2 = colorsys.rgb_to_hsv(r2,g2,b2)
-
+
return 360*h1,360*(h2-h1),s1,(s2-s1),v1,(v2-v1)
palettes = {
@@ -167,7 +167,7 @@ def is_ranking_piece(workout):
return True
elif workout.duration in rankingdurations:
return True
-
+
return False
def range_to_color_hex(groupcols,palette='monochrome_blue'):
@@ -176,20 +176,20 @@ def range_to_color_hex(groupcols,palette='monochrome_blue'):
plt = palettes[palette]
except KeyError:
plt = palettes['monochrome_blue']
-
+
rgb = [colorsys.hsv_to_rgb((plt[0]+plt[1]*x)/360.,
plt[2]+plt[3]*x,
plt[4]+plt[5]*x) for x in groupcols]
RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb]
colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB]
-
+
return colors
def str2bool(v):
return v.lower() in ("yes", "true", "t", "1")
-def uniqify(seq, idfun=None):
+def uniqify(seq, idfun=None):
# order preserving
if idfun is None:
def idfun(x): return x
@@ -222,12 +222,12 @@ def geo_distance(lat1,lon1,lat2,lon2):
This is a slight underestimate but is close enough for our purposes,
We're never moving more than 10 meters between trackpoints
- Bearing calculation fails if one of the points is a pole.
- (Hey, from the North pole you can walk South, East, North and end up
+ Bearing calculation fails if one of the points is a pole.
+ (Hey, from the North pole you can walk South, East, North and end up
on the same spot!)
-
+
"""
-
+
# radius of our earth in km --> should be moved to settings if
# rowing takes off on other planets
R = 6373.0
@@ -257,7 +257,7 @@ def geo_distance(lat1,lon1,lat2,lon2):
return [distance,bearing]
-
+
def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
pwr = abs(p0)/(1+(delta/abs(p2)))
@@ -268,7 +268,7 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
pwr *= ratio
-
+
delta = delta.values.astype(int)
cpvalues = cpvalues.values.astype(int)
pwr = pwr.astype(int)
@@ -276,7 +276,7 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
res = np.sum(cpvalues>pwr)
res2 = np.sum(cpvalues>pwr2)
-
+
btdf = pd.DataFrame(
{
'delta':delta[cpvalues>pwr],
@@ -288,7 +288,7 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
btdf.sort_values('delta',axis=0,inplace=True)
-
+
return res>1,btdf,res2>1
@@ -300,7 +300,7 @@ def myqueue(queue,function,*args,**kwargs):
def revoke(self):
return 1
-
+
if settings.TESTING:
return MockJob()
elif settings.CELERY:
@@ -310,7 +310,7 @@ def myqueue(queue,function,*args,**kwargs):
else:
if settings.DEBUG:
kwargs['debug'] = True
-
+
job_id = str(uuid.uuid4())
kwargs['job_id'] = job_id
kwargs['jobkey'] = job_id
@@ -335,7 +335,7 @@ def my_dict_from_instance(instance,model):
thedict['id'] = instance.id
for f in instance._meta.fields:
-
+
fname = f.name
try:
@@ -405,24 +405,13 @@ def totaltime_sec_to_string(totaltime):
return duration
-def isprorower(r):
- result = False
- result = r.rowerplan in ['pro','coach','plan']
-
- if not result and r.protrialexpires:
- result = r.rowerplan == 'basic' and r.protrialexpires >= datetime.date.today()
- if not result and r.rowerplan == 'freecoach':
- if r.mycoachgroup is not None:
- result = len(r.mycoachgroup)>=4
-
- return result
def iscoach(m,r):
result = False
result = m in r.coaches
return result
-
+
# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
@@ -433,7 +422,7 @@ def ewmovingaverage(interval,window_size):
intervaldf = pd.DataFrame({'v':interval})
idf_ewma1 = intervaldf.ewm(span=window_size)
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
-
+
i_ewma1 = idf_ewma1.mean().loc[:,'v']
i_ewma2 = idf_ewma2.mean().loc[:,'v']
@@ -478,7 +467,7 @@ def custom_exception_handler(exc,message):
res.json = json.dumps(response)
return res
-
+
def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high'):
authorizationstring = str('Bearer ' + r.stravatoken)
headers = {'Authorization': authorizationstring,
@@ -503,7 +492,7 @@ def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high
return np.array(data['data'])
except TypeError:
return None
-
+
return None
def allmonths(startdate,enddate):
@@ -518,4 +507,3 @@ def allsundays(startdate,enddate):
while d<=enddate:
yield d
d += datetime.timedelta(days=7)
-
diff --git a/rowers/views/exportviews.py b/rowers/views/exportviews.py
index 4b5f4684..08f2e064 100644
--- a/rowers/views/exportviews.py
+++ b/rowers/views/exportviews.py
@@ -1,18 +1,17 @@
from rowers.views.statements import *
-
+
# Export workout to TCX and send to user's email address
-@login_required()
+@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id'))
def workout_tcxemail_view(request,id=0):
r = getrower(request.user)
w = get_workout(id)
- if not checkworkoutuser(request.user,w):
- raise PermissionDenied("Access denied")
+
row = rdata(w.csvfilename)
@@ -31,8 +30,8 @@ def workout_tcxemail_view(request,id=0):
return response
-
-
+
+
@login_required()
def plannedsessions_icsemail_view(request,userid=0):
@@ -66,7 +65,7 @@ def plannedsessions_icsemail_view(request,userid=0):
d1 = startdate.strftime("%Y%m%d"),
d2 = enddate.strftime("%Y%m%d"),
)
-
+
response['Content-Type'] = 'application/octet-stream'
return response
@@ -96,7 +95,7 @@ def plannedsessions_coach_icsemail_view(request,userid=0):
rowers += ps.rower.filter(team__in=rteams).exclude(rowerplan='freecoach')
rowers = list(set(rowers))
-
+
cal = Calendar()
cal.add('prodid','rowsandall')
cal.add('version','1.0')
@@ -169,13 +168,12 @@ def course_kmldownload_view(request,id=0):
# Export workout to GPX and send to user's email address
-@login_required()
+@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id'))
def workout_gpxemail_view(request,id=0):
r = getrower(request.user)
w = get_workout(id)
- if not checkworkoutuser(request.user,w):
- raise PermissionDenied("Access denied")
+
row = rdata(w.csvfilename)
@@ -233,17 +231,16 @@ def workouts_summaries_email_view(request):
{
'form':form
})
-
+
# Get Workout CSV file and send it to user's email address
-@login_required()
+@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id'))
def workout_csvemail_view(request,id=0):
r = getrower(request.user)
w = get_workout(id)
- if not checkworkoutuser(request.user,w):
- raise PermissionDenied("Access denied")
+
rowdata = rdata(w.csvfilename)
code = str(uuid4())
@@ -254,13 +251,13 @@ def workout_csvemail_view(request,id=0):
df = rowdata.df
df[' ElapsedTime (sec)'] = df['TimeStamp (sec)']
df['TimeStamp (sec)'] = df['TimeStamp (sec)'] + starttimeunix
-
+
response = HttpResponse(df.to_csv())
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
response['Content-Type'] = 'application/octet-stream'
return response
-
+
# Get Workout CSV file and send it to user's email address
@login_required()
diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py
index 2f97d00c..7b77b68b 100644
--- a/rowers/views/importviews.py
+++ b/rowers/views/importviews.py
@@ -8,14 +8,14 @@ from rowers.views.statements import *
import numpy
def default(o):
- if isinstance(o, numpy.int64): return int(o)
+ if isinstance(o, numpy.int64): return int(o)
raise TypeError
# Send workout to TP
-@login_required()
+@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id'))
def workout_tp_upload_view(request,id=0):
-
+
message = ""
r = getrower(request.user)
res = -1
@@ -25,52 +25,48 @@ def workout_tp_upload_view(request,id=0):
return HttpResponseRedirect("/rowers/me/tpauthorize/")
# ready to upload. Hurray
- w = get_workout_permitted(request.user,id)
+ w = get_object_or_404(Workout,pk=id)
r = w.user
- if (checkworkoutuser(request.user,w)):
- tcxfile = tpstuff.createtpworkoutdata(w)
- if tcxfile:
- res,reason,status_code,headers = tpstuff.uploadactivity(
- r.tptoken,tcxfile,
- name=w.name
- )
- if res == 0:
- message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason
- try:
- os.remove(tcxfile)
- except WindowsError:
- pass
- messages.error(request,message)
-
- else: # res != 0
- w.uploadedtotp = res
- w.save()
+ tcxfile = tpstuff.createtpworkoutdata(w)
+ if tcxfile:
+ res,reason,status_code,headers = tpstuff.uploadactivity(
+ r.tptoken,tcxfile,
+ name=w.name
+ )
+ if res == 0:
+ message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason
+ try:
os.remove(tcxfile)
- messages.info(request,'Uploaded to TrainingPeaks')
+ except WindowsError:
+ pass
- else: # no tcxfile
- message = "Upload to TrainingPeaks failed"
- w.uploadedtotp = -1
- w.save()
messages.error(request,message)
- else: # not allowed to upload
- message = "You are not allowed to export this workout to TP"
+ else: # res != 0
+ w.uploadedtotp = res
+ w.save()
+ os.remove(tcxfile)
+ messages.info(request,'Uploaded to TrainingPeaks')
+
+ else: # no tcxfile
+ message = "Upload to TrainingPeaks failed"
+ w.uploadedtotp = -1
+ w.save()
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
})
-
+
return HttpResponseRedirect(url)
-
-
+
+
# Send workout to Strava
# abundance of error logging here because there were/are some bugs
-@login_required()
+@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id'))
def workout_strava_upload_view(request,id=0):
message = ""
r = getrower(request.user)
@@ -80,7 +76,7 @@ def workout_strava_upload_view(request,id=0):
thetoken = strava_open(request.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
-
+
if (r.stravatoken == '') or (r.stravatoken is None):
s = "Token doesn't exist. Need to authorize"
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
@@ -88,94 +84,94 @@ def workout_strava_upload_view(request,id=0):
# ready to upload. Hurray
w = get_workout_permitted(request.user,id)
r = w.user
- if (checkworkoutuser(request.user,w)):
- try:
- tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w)
- if tcxfile:
- with open(tcxfile,'rb') as f:
+
+ try:
+ tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w)
+ if tcxfile:
+ with open(tcxfile,'rb') as f:
+ try:
+ newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
+ except TypeError:
+ newnotes = 'from '+w.workoutsource+' via rowsandall.com'
+ if w.workouttype in mytypes.rowtypes:
+ activity_type = r.stravaexportas
+ else:
try:
- newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
- except TypeError:
- newnotes = 'from '+w.workoutsource+' via rowsandall.com'
- if w.workouttype in mytypes.rowtypes:
- activity_type = r.stravaexportas
- else:
- try:
- activity_type = mytypes.stravamapping[w.workouttype]
- except KeyError:
- activity_type = 'Ride'
-
- res,mes = stravastuff.handle_stravaexport(
- f,w.name,
- r.stravatoken,
- description=newnotes,
- activity_type=activity_type)
- if res==0:
- messages.error(request,mes)
- w.uploadedtostrava = -1
- w.save()
- try:
- os.remove(tcxfile)
- except WindowsError:
- pass
- url = reverse(r.defaultlandingpage,
- kwargs = {
- 'id':encoder.encode_hex(w.id),
- })
- response = HttpResponseRedirect(url)
- return response
-
+ activity_type = mytypes.stravamapping[w.workouttype]
+ except KeyError:
+ activity_type = 'Ride'
+
+ res,mes = stravastuff.handle_stravaexport(
+ f,w.name,
+ r.stravatoken,
+ description=newnotes,
+ activity_type=activity_type)
+ if res==0:
+ messages.error(request,mes)
+ w.uploadedtostrava = -1
+ w.save()
try:
- w.uploadedtostrava = res
- w.save()
- try:
- os.remove(tcxfile)
- except WindowsError:
- pass
- url = reverse('workout_edit_view',kwargs={'id':w.id})
+ os.remove(tcxfile)
+ except WindowsError:
+ pass
+ url = reverse(r.defaultlandingpage,
+ kwargs = {
+ 'id':encoder.encode_hex(w.id),
+ })
+ response = HttpResponseRedirect(url)
+ return response
+
+ try:
+ w.uploadedtostrava = res
+ w.save()
+ try:
+ os.remove(tcxfile)
+ except WindowsError:
+ pass
+ url = reverse('workout_edit_view',kwargs={'id':w.id})
- messages.info(request,mes)
- except:
- with open("media/stravaerrors.log","a") as errorlog:
- errorstring = str(sys.exc_info()[0])
- timestr = strftime("%Y%m%d-%H%M%S")
- errorlog.write(timestr+errorstring+"\r\n")
- errorlog.write("views.py line 826\r\n")
- message = 'Error: '+errorstring
- messages.error(request,message)
- else: # No tcxfile
- message = "Strava Data error "+tcxmessg
- messages.error(request,message)
- w.uploadedtostrava = -1
- w.save()
- url = reverse(r.defaultlandingpage,
- kwargs = {
- 'id':encoder.encode_hex(w.id),
- })
- response = HttpResponseRedirect(url)
-
-
- url = reverse(r.defaultlandingpage,
- kwargs = {
- 'id':encoder.encode_hex(w.id),
- }
- )
- response = HttpResponseRedirect(url)
- except ActivityUploadFailed as e:
- message = "Strava Upload error: %s" % e
+ messages.info(request,mes)
+ except:
+ with open("media/stravaerrors.log","a") as errorlog:
+ errorstring = str(sys.exc_info()[0])
+ timestr = strftime("%Y%m%d-%H%M%S")
+ errorlog.write(timestr+errorstring+"\r\n")
+ errorlog.write("views.py line 826\r\n")
+ message = 'Error: '+errorstring
+ messages.error(request,message)
+ else: # No tcxfile
+ message = "Strava Data error "+tcxmessg
messages.error(request,message)
w.uploadedtostrava = -1
w.save()
- os.remove(tcxfile)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
- })
+ })
response = HttpResponseRedirect(url)
- return response
-
+
+ url = reverse(r.defaultlandingpage,
+ kwargs = {
+ 'id':encoder.encode_hex(w.id),
+ }
+ )
+ response = HttpResponseRedirect(url)
+ except ActivityUploadFailed as e:
+ message = "Strava Upload error: %s" % e
+ messages.error(request,message)
+ w.uploadedtostrava = -1
+ w.save()
+ os.remove(tcxfile)
+ url = reverse(r.defaultlandingpage,
+ kwargs = {
+ 'id':encoder.encode_hex(w.id),
+ })
+ response = HttpResponseRedirect(url)
+
+ return response
+
# Upload workout to Concept2 logbook
@login_required()
def workout_c2_upload_view(request,id=0):
@@ -183,7 +179,7 @@ def workout_c2_upload_view(request,id=0):
# ready to upload. Hurray
w = get_workout(id)
r = w.user
-
+
try:
message,c2id = c2stuff.workout_c2_upload(request.user,w)
except NoTokenError:
@@ -199,14 +195,14 @@ def workout_c2_upload_view(request,id=0):
kwargs = {
'id':encoder.encode_hex(w.id)
})
-
+
response = HttpResponseRedirect(url)
return response
# Upload workout to RunKeeper
-@login_required()
+@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id'))
def workout_runkeeper_upload_view(request,id=0):
message = ""
w = get_workout(id)
@@ -219,48 +215,45 @@ def workout_runkeeper_upload_view(request,id=0):
# ready to upload. Hurray
- if (checkworkoutuser(request.user,w)):
- data = runkeeperstuff.createrunkeeperworkoutdata(w)
- if not data:
- message = "Data error"
- messages.error(request,message)
- url = reverse(r.defaultlandingpage,
- kwargs = {
- 'id':id,
- })
- return HttpResponseRedirect(url)
-
- authorizationstring = str('Bearer ' + thetoken)
- headers = {'Authorization': authorizationstring,
- 'user-agent': 'sanderroosendaal',
- 'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
- 'Content-Length':'nnn'}
- url = "https://api.runkeeper.com/fitnessActivities"
- response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
-
- # check for duplicate error first
- if (response.status_code == 409 ):
- message = "Duplicate error"
- messages.error(request,message)
- w.uploadedtorunkeeper = -1
- w.save()
- elif (response.status_code == 201 or response.status_code==200):
- runkeeperid = runkeeperstuff.getidfromresponse(response)
- w.uploadedtorunkeeper = runkeeperid
- w.save()
- url = reverse('workout_edit_view',
- kwargs={'id':encoder.encode_hex(w.id)})
-
- return HttpResponseRedirect(url)
- else:
- s = response
- message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text)
- messages.error(request,message)
-
- else:
- message = "You are not authorized to upload this workout"
+ data = runkeeperstuff.createrunkeeperworkoutdata(w)
+ if not data:
+ message = "Data error"
messages.error(request,message)
+ url = reverse(r.defaultlandingpage,
+ kwargs = {
+ 'id':id,
+ })
+ return HttpResponseRedirect(url)
+
+ authorizationstring = str('Bearer ' + thetoken)
+ headers = {'Authorization': authorizationstring,
+ 'user-agent': 'sanderroosendaal',
+ 'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
+ 'Content-Length':'nnn'}
+
+ url = "https://api.runkeeper.com/fitnessActivities"
+ response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
+
+ # check for duplicate error first
+ if (response.status_code == 409 ):
+ message = "Duplicate error"
+ messages.error(request,message)
+ w.uploadedtorunkeeper = -1
+ w.save()
+ elif (response.status_code == 201 or response.status_code==200):
+ runkeeperid = runkeeperstuff.getidfromresponse(response)
+ w.uploadedtorunkeeper = runkeeperid
+ w.save()
+ url = reverse('workout_edit_view',
+ kwargs={'id':encoder.encode_hex(w.id)})
+
+ return HttpResponseRedirect(url)
+ else:
+ s = response
+ message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text)
+ messages.error(request,message)
+
url = reverse(r.defaultlandingpage,
kwargs = {
@@ -270,7 +263,7 @@ def workout_runkeeper_upload_view(request,id=0):
return HttpResponseRedirect(url)
# Upload workout to Underarmour
-@login_required()
+@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id'))
def workout_underarmour_upload_view(request,id=0):
message = ""
w = get_workout(id)
@@ -282,48 +275,45 @@ def workout_underarmour_upload_view(request,id=0):
return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
# ready to upload. Hurray
-
- if (checkworkoutuser(request.user,w)):
- data = underarmourstuff.createunderarmourworkoutdata(w)
- if not data:
- message = "Data error"
- messages.error(request,message)
- url = reverse(r.defaultlandingpage,
- kwargs = {
- 'id':encoder.encode_hex(w.id),
- })
- return HttpResponseRedirect(url)
-
- authorizationstring = str('Bearer ' + thetoken)
- headers = {'Authorization': authorizationstring,
- 'Api-Key': UNDERARMOUR_CLIENT_KEY,
- 'user-agent': 'sanderroosendaal',
- 'Content-Type': 'application/json',
- }
- url = "https://api.ua.com/v7.1/workout/"
- response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
-
- # check for duplicate error first
- if (response.status_code == 409 ):
- message = "Duplicate error"
- messages.error(request,message)
- w.uploadedtounderarmour = -1
- w.save()
- elif (response.status_code == 201 or response.status_code==200):
- underarmourid = underarmourstuff.getidfromresponse(response)
- w.uploadedtounderarmour = underarmourid
- w.save()
- url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)})
+ data = underarmourstuff.createunderarmourworkoutdata(w)
+ if not data:
+ message = "Data error"
+ messages.error(request,message)
+ url = reverse(r.defaultlandingpage,
+ kwargs = {
+ 'id':encoder.encode_hex(w.id),
+ })
+ return HttpResponseRedirect(url)
- return HttpResponseRedirect(url)
- else:
- s = response
- message = "Something went wrong in workout_underarmour_upload_view: %s " % s.reason
- messages.error(request,message)
+ authorizationstring = str('Bearer ' + thetoken)
+ headers = {'Authorization': authorizationstring,
+ 'Api-Key': UNDERARMOUR_CLIENT_KEY,
+ 'user-agent': 'sanderroosendaal',
+ 'Content-Type': 'application/json',
+ }
+
+ url = "https://api.ua.com/v7.1/workout/"
+ response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
+
+
+ # check for duplicate error first
+ if (response.status_code == 409 ):
+ message = "Duplicate error"
+ messages.error(request,message)
+ w.uploadedtounderarmour = -1
+ w.save()
+ elif (response.status_code == 201 or response.status_code==200):
+ underarmourid = underarmourstuff.getidfromresponse(response)
+ w.uploadedtounderarmour = underarmourid
+ w.save()
+ url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)})
+
+ return HttpResponseRedirect(url)
else:
- message = "You are not authorized to upload this workout"
+ s = response
+ message = "Something went wrong in workout_underarmour_upload_view: %s " % s.reason
messages.error(request,message)
url = reverse(r.defaultlandingpage,
@@ -334,7 +324,7 @@ def workout_underarmour_upload_view(request,id=0):
return HttpResponseRedirect(url)
# Upload workout to SportTracks
-@login_required()
+@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id'))
def workout_sporttracks_upload_view(request,id=0):
message = ""
# ready to upload. Hurray
@@ -347,49 +337,46 @@ def workout_sporttracks_upload_view(request,id=0):
return HttpResponseRedirect("/rowers/me/sporttracksauthorize/")
- if (checkworkoutuser(request.user,w)):
- data = sporttracksstuff.createsporttracksworkoutdata(w)
- if not data:
- message = "Data error"
- messages.error(request,message)
- url = reverse(r.defaultlandingpage,
- kwargs = {
- 'id':encoder.encode_hex(w.id),
- })
- return HttpResponseRedirect(url)
+ data = sporttracksstuff.createsporttracksworkoutdata(w)
- authorizationstring = str('Bearer ' + thetoken)
- headers = {'Authorization': authorizationstring,
- 'user-agent': 'sanderroosendaal',
- 'Content-Type': 'application/json'}
+ if not data:
+ message = "Data error"
+ messages.error(request,message)
+ url = reverse(r.defaultlandingpage,
+ kwargs = {
+ 'id':encoder.encode_hex(w.id),
+ })
+ return HttpResponseRedirect(url)
- url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
- response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
+ authorizationstring = str('Bearer ' + thetoken)
+ headers = {'Authorization': authorizationstring,
+ 'user-agent': 'sanderroosendaal',
+ 'Content-Type': 'application/json'}
-
- # check for duplicate error first
- if (response.status_code == 409 ):
- message = "Duplicate error"
- messages.error(request,message)
- w.uploadedtosporttracks = -1
- w.save()
- elif (response.status_code == 201 or response.status_code==200):
- s= response.json()
- sporttracksid = sporttracksstuff.getidfromresponse(response)
- w.uploadedtosporttracks = sporttracksid
- w.save()
- message = "Upload to SportTracks was successful"
- messages.info(request,message)
+ url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
+ response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
- url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)})
- return HttpResponseRedirect(url)
- else:
- s = response
- message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason
- messages.error(request,message)
+
+ # check for duplicate error first
+ if (response.status_code == 409 ):
+ message = "Duplicate error"
+ messages.error(request,message)
+ w.uploadedtosporttracks = -1
+ w.save()
+ elif (response.status_code == 201 or response.status_code==200):
+ s= response.json()
+ sporttracksid = sporttracksstuff.getidfromresponse(response)
+ w.uploadedtosporttracks = sporttracksid
+ w.save()
+ message = "Upload to SportTracks was successful"
+ messages.info(request,message)
+
+ url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)})
+ return HttpResponseRedirect(url)
else:
- message = "You are not authorized to upload this workout"
+ s = response
+ message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason
messages.error(request,message)
url = reverse(r.defaultlandingpage,
@@ -399,7 +386,7 @@ def workout_sporttracks_upload_view(request,id=0):
return HttpResponseRedirect(url)
-# Concept2 authorization
+# Concept2 authorization
@login_required()
def rower_c2_authorize(request):
# Generate a random string for the state parameter
@@ -444,10 +431,10 @@ def rower_polar_authorize(request):
# "scope":"accesslink.read_all"
}
url = "https://flow.polar.com/oauth2/authorization?" +urllib.parse.urlencode(params)
-
+
return HttpResponseRedirect(url)
-
+
# Runkeeper authorization
@login_required()
@@ -494,7 +481,7 @@ def rower_underarmour_authorize(request):
state = str(uuid4())
redirect_uri = UNDERARMOUR_REDIRECT_URI
-
+
url = 'https://www.mapmyfitness.com/v7.1/oauth2/authorize/?' \
'client_id={0}&response_type=code&redirect_uri={1}'.format(
UNDERARMOUR_CLIENT_KEY, redirect_uri
@@ -647,7 +634,7 @@ def rower_process_callback(request):
url = reverse('workouts_view')
return HttpResponseRedirect(url)
-
+
access_token = res[0]
if access_token == 0:
message = res[1]
@@ -657,8 +644,8 @@ def rower_process_callback(request):
url = reverse('workouts_view')
return HttpResponseRedirect(url)
-
-
+
+
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
@@ -674,12 +661,12 @@ def rower_process_callback(request):
messages.info(request,successmessage)
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
-# dummy
+# dummy
@login_required()
def rower_process_twittercallback(request):
return "dummy"
@@ -697,13 +684,13 @@ def rower_process_polarcallback(request):
messages.error(request,message)
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
access_token, expires_in, user_id = polarstuff.get_token(code)
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
-
+
r = getrower(request.user)
r.polartoken = access_token
r.polartokenexpirydate = expirydatetime
@@ -714,7 +701,7 @@ def rower_process_polarcallback(request):
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
@@ -734,10 +721,10 @@ def rower_process_stravacallback(request):
messages.error(request,message)
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
-
+
res = stravastuff.get_token(code)
if res[0]:
@@ -762,7 +749,7 @@ def rower_process_stravacallback(request):
message = "Something went wrong with the Strava authorization"
messages.error(request,message)
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
@@ -776,7 +763,7 @@ def rower_process_runkeepercallback(request):
if access_token == 0:
messages.error(request,"Something went wrong importing the token")
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
@@ -789,7 +776,7 @@ def rower_process_runkeepercallback(request):
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
@@ -806,7 +793,7 @@ def rower_process_sporttrackscallback(request):
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
-
+
r = getrower(request.user)
r.sporttrackstoken = access_token
r.sporttrackstokenexpirydate = expirydatetime
@@ -817,7 +804,7 @@ def rower_process_sporttrackscallback(request):
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
@@ -833,7 +820,7 @@ def rower_process_underarmourcallback(request):
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
-
+
r = getrower(request.user)
r.underarmourtoken = access_token
r.underarmourtokenexpirydate = expirydatetime
@@ -844,7 +831,7 @@ def rower_process_underarmourcallback(request):
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
@@ -859,7 +846,7 @@ def rower_process_tpcallback(request):
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
-
+
r = getrower(request.user)
r.tptoken = access_token
r.tptokenexpirydate = expirydatetime
@@ -870,7 +857,7 @@ def rower_process_tpcallback(request):
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
@@ -892,7 +879,7 @@ def rower_process_testcallback(request):
text += "\n\nRefresh Token:\n"
text += refresh_token
-
+
return HttpResponse(text)
@@ -909,10 +896,10 @@ def workout_stravaimport_view(request,message="",userid=0):
except NoTokenError:
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
-
+
res = stravastuff.get_strava_workout_list(request.user)
-
+
if (res.status_code != 200):
if (res.status_code == 401):
r = getrower(request.user)
@@ -950,7 +937,7 @@ def workout_stravaimport_view(request,message="",userid=0):
w.uploadedtostrava = int(stravaid)
w.save()
-
+
knownstravaids = uniqify([
w.uploadedtostrava for w in Workout.objects.filter(user=r)
])
@@ -985,7 +972,7 @@ def workout_stravaimport_view(request,message="",userid=0):
r = getrower(request.user)
-
+
return render(request,'strava_list_import.html',
{'workouts':workouts,
'rower':r,
@@ -1024,7 +1011,7 @@ def workout_runkeeperimport_view(request,message="",userid=0):
r = item['type']
keys = ['id','distance','duration','starttime','type']
values = [i,d,ttot,s,r]
-
+
res = dict(zip(keys,values))
workouts.append(res)
@@ -1072,11 +1059,11 @@ def workout_underarmourimport_view(request,message="",userid=0):
ttot = item['aggregates']['active_time_total']
except KeyError:
ttot = 0
-
+
keys = ['id','distance','duration','starttime','type']
values = [i,d,ttot,s,r]
thedict = dict(zip(keys,values))
-
+
workouts.append(thedict)
rower = getrower(request.user)
@@ -1090,7 +1077,7 @@ def workout_underarmourimport_view(request,message="",userid=0):
'name':'Concept2'
},
]
-
+
return render(request,'underarmour_list_import.html',
{'workouts':workouts,
'breadcrumbs':breadcrumbs,
@@ -1115,13 +1102,13 @@ def workout_polarimport_view(request,userid=0):
return HttpResponseRedirect(url)
except:
pass
-
+
for exercise in exercises:
try:
d = exercise['distance']
except KeyError:
d = 0
-
+
i = exercise['id']
transactionid = exercise['transaction-id']
starttime = exercise['start-time']
@@ -1156,13 +1143,13 @@ def workout_polarimport_view(request,userid=0):
})
-
+
# The page where you select which SportTracks workout to import
@login_required()
def workout_sporttracksimport_view(request,message="",userid=0):
-
+
res = sporttracksstuff.get_sporttracks_workout_list(request.user)
if (res.status_code != 200):
if (res.status_code == 401):
@@ -1215,7 +1202,7 @@ def workout_sporttracksimport_view(request,message="",userid=0):
'name':'SportTracks'
},
]
-
+
return render(request,'sporttracks_list_import.html',
{'workouts':workouts,
'breadcrumbs':breadcrumbs,
@@ -1225,7 +1212,7 @@ def workout_sporttracksimport_view(request,message="",userid=0):
})
return HttpResponse(res)
-
+
# List of workouts on Concept2 logbook. This view only used for debugging
@login_required()
def c2listdebug_view(request,page=1,message=""):
@@ -1262,7 +1249,7 @@ def c2listdebug_view(request,page=1,message=""):
res = dict(zip(keys,values))
workouts.append(res)
-
+
return render(request,
'c2_list_import2.html',
{'workouts':workouts,
@@ -1288,20 +1275,20 @@ def workout_getc2workout_all(request,page=1,message=""):
alldata = {}
for item in res.json()['data']:
alldata[item['id']] = item
-
+
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:
workoutid = c2stuff.create_async_workout(alldata,
request.user,c2id)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
-
-
+
+
# List of workouts available on Concept2 logbook - for import
@login_required()
def workout_c2import_view(request,page=1,userid=0,message=""):
@@ -1312,7 +1299,7 @@ def workout_c2import_view(request,page=1,userid=0,message=""):
messages.info(request,"You cannot import other people's workouts from Concept2")
r = getrower(request.user)
-
+
try:
thetoken = c2_open(request.user)
except NoTokenError:
@@ -1366,7 +1353,7 @@ def workout_c2import_view(request,page=1,userid=0,message=""):
]
r = getrower(request.user)
-
+
return render(request,
'c2_list_import2.html',
{'workouts':workouts,
@@ -1394,14 +1381,14 @@ def workout_getimportview(request,externalid,source = 'c2'):
if not res[0]:
messages.error(request,res[1])
url = reverse('workouts_view')
-
+
return HttpResponseRedirect(url)
strokedata = res[1]
data = res[0]
-
+
# Now works only for C2
try:
if strokedata == 0:
@@ -1433,16 +1420,16 @@ def workout_getimportview(request,externalid,source = 'c2'):
if timezone_str is None:
timezone_str = 'UTC'
-
+
workoutdate = startdatetime.astimezone(
pytz.timezone(timezone_str)
).strftime('%Y-%m-%d')
starttime = startdatetime.astimezone(
pytz.timezone(timezone_str)
).strftime('%H:%M:%S')
-
+
r = getrower(request.user)
-
+
id, message = dataprep.create_row_df(r,
distance,
duration,
@@ -1463,7 +1450,7 @@ def workout_getimportview(request,externalid,source = 'c2'):
})
return HttpResponseRedirect(url)
-
+
# strokedata not empty - continue
id,message = importsources[source].add_workout_from_data(
request.user,
@@ -1504,7 +1491,7 @@ def workout_getimportview(request,externalid,source = 'c2'):
rowdata.write_csv(w.csvfilename,gzip=True)
dataprep.update_strokedata(w.id,rowdata.df)
-
+
if source == 'strava':
w.uploadedtostrava = externalid
@@ -1534,7 +1521,7 @@ def workout_getimportview(request,externalid,source = 'c2'):
})
return HttpResponseRedirect(url)
-
+
@@ -1599,7 +1586,7 @@ def workout_getstravaworkout_next(request):
alldata = {}
for item in res.json():
alldata[item['id']] = item
-
+
knownstravaids = uniqify([
w.uploadedtostrava for w in Workout.objects.filter(user=r)
])
@@ -1608,10 +1595,8 @@ def workout_getstravaworkout_next(request):
theid = newids[0]
workoutid = stravastuff.create_async_workout(alldata,r.user,stravaid,debug=True)
-
-
+
+
url = reverse('workouts_view')
return HttpResponseRedirect(url)
-
-
diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py
index 05910fbc..aa158fcf 100644
--- a/rowers/views/planviews.py
+++ b/rowers/views/planviews.py
@@ -1697,21 +1697,16 @@ def plannedsession_edit_view(request,id=0,userid=0):
})
-@login_required()
+@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id'))
def plannedsession_detach_view(request,id=0,psid=0):
r = getrequestrower(request)
- try:
- ps = PlannedSession.objects.get(id=psid)
- except PlannedSession.DoesNotExist:
- raise Http404("Planned Session does not exist")
+
+ ps = get_object_or_404(PlannedSession,pk=psid)
w = get_workout(id)
- if (checkworkoutuser(request.user,w)==False):
- return HttpResponseForbidden("Permission Error")
-
remove_workout_plannedsession(w,ps)
url = reverse(plannedsession_view,kwargs={'id':psid})
diff --git a/rowers/views/statements.py b/rowers/views/statements.py
index fa81fc24..ac3867da 100644
--- a/rowers/views/statements.py
+++ b/rowers/views/statements.py
@@ -32,10 +32,17 @@ from icalendar import Calendar, Event
from functools import reduce
+from rules.contrib.views import PermissionRequiredMixin
+
import rowers.braintreestuff as braintreestuff
import rowers.payments as payments
from rowers.opaque import encoder
+from rowers.rower_rules import (
+ ispromember,is_coach_user,is_team_member,is_rower_team_member,
+ is_workout_user
+ )
+
from django.shortcuts import render
from django.template.loader import render_to_string
@@ -98,7 +105,7 @@ from rowers.models import (
TrainingMesoCycleForm, TrainingMicroCycleForm,
RaceLogo,RowerBillingAddressForm,PaidPlan,
AlertEditForm, ConditionEditForm,
- PlannedSessionComment,CoachRequest,CoachOffer,checkaccessplanuser,
+ PlannedSessionComment,CoachRequest,CoachOffer,
VideoAnalysis
)
from rowers.models import (
@@ -275,6 +282,11 @@ def getfavorites(r,row):
return favorites,maxfav
+def get_workout_by_opaqueid(request,id,**kwargs):
+ pk = encoder.decode_hex(id)
+ return get_object_or_404(Workout,pk=pk)
+
+
def get_workout_default_page(request,id):
if request.user.is_anonymous:
return reverse('workout_view',kwargs={'id':id})
@@ -310,7 +322,7 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False):
except Rower.DoesNotExist:
raise Http404("Rower doesn't exist")
- if not checkaccessuser(request.user,r):
+ if userid != 0 and not is_coach_user(u,r):
raise PermissionDenied("You have no access to this user")
if notpermanent == False:
@@ -343,7 +355,7 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False):
except Rower.DoesNotExist:
raise Http404("Rower doesn't exist")
- if not checkaccessplanuser(request.user,r):
+ if not is_coach_user(request.user,r):
raise PermissionDenied("You have no access to this user")
if notpermanent == False:
@@ -377,21 +389,6 @@ def get_workout(id):
return w
-def get_workout_permitted(user,id):
- w = get_workout(id)
-
- if (checkworkoutuser(user,w)==False):
- raise PermissionDenied("Access denied")
-
- return w
-
-def get_workout_permittedview(user,id):
- w = get_workout(id)
-
- if (checkworkoutuserview(user,w)==False):
- raise PermissionDenied("Access denied")
-
- return w
def getvalue(data):
perc = 0
@@ -774,9 +771,9 @@ def get_stored_tasks_status(request):
return taskstatus
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def get_thumbnails(request,id):
- row = get_workout_permitted(request.user,id)
+ row = get_workout_by_opaqueid(request,id)
r = getrower(request.user)
@@ -861,27 +858,6 @@ def get_blog_posts_old(request):
-@login_required()
-def get_testscript(request,id):
- row = get_workout_permitted(request.user,id)
- r = getrower(request.user)
-
- object = {
- "script":"""
-
-
-
- """,
- "div":"""
-
-Hoi
-
-"""
- }
-
-
- return JSONResponse([object,object])
-
@login_required()
def session_jobs_view(request):
taskstatus = get_stored_tasks_status(request)
@@ -990,10 +966,6 @@ from rowers.utils import (
import rowers.datautils as datautils
-from rowers.models import (
- checkworkoutuser,checkaccessuser,checkviewworkouts,checkworkoutuserview
- )
-
# Check if a user is a Coach member
def iscoachmember(user):
if not user.is_anonymous:
@@ -1044,21 +1016,7 @@ def hasplannedsessions(user):
return result
from rowers.utils import ProcessorCustomerError
-from rowers.utils import isprorower
-# Check if a user is a Pro member
-def ispromember(user):
- if user and not user.is_anonymous:
- try:
- r = Rower.objects.get(user=user)
- except Rower.DoesNotExist:
- r = Rower(user=user)
- r.save()
-
- result = user.is_authenticated and isprorower(r)
- else:
- result = False
- return result
# More User/Rower utils
def add_defaultfavorites(r):
diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py
index f72537f9..36332560 100644
--- a/rowers/views/workoutviews.py
+++ b/rowers/views/workoutviews.py
@@ -64,8 +64,10 @@ def workout_video_view_mini(request,id=''):
else:
mode = 'erg'
+
+
if request.user.is_authenticated:
- mayedit = checkworkoutuser(request.user,w) and isprorower(request.user.rower)
+ mayedit = is_workout_user(request.user,w) and is_promember(request.user)
rower = request.user.rower
else:
mayedit = False
@@ -176,7 +178,7 @@ def workout_video_view(request,id=''):
mode = 'erg'
if request.user.is_authenticated:
- mayedit = checkworkoutuser(request.user,w) and isprorower(request.user.rower)
+ mayedit = is_promember(request.user) and is_workout_user(request.user,w)
rower = request.user.rower
else:
mayedit = False
@@ -271,20 +273,18 @@ def workout_video_view(request,id=''):
# Create a video compared with data
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember,login_url="/rowers/paidplans/",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_video_create_view(request,id=0):
# get workout
- w = get_workout_permitted(request.user,id)
+ w = get_workout_by_opaqueid(request,id)
if w.workouttype in mytypes.otwtypes:
mode = 'water'
else:
mode = 'erg'
-
- mayedit = checkworkoutuser(request.user,w) and isprorower(request.user.rower)
-
# get video ID and offset
if request.method == 'POST':
form = VideoAnalysisCreateForm(request.POST)
@@ -457,12 +457,10 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False):
})
# Switch from GPS to Impeller (only for SpeedCoach 2, if impeller data)
-@login_required
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def otw_use_impeller(request,id=0):
w = get_workout(id)
- if (checkworkoutuser(request.user,w)==False):
- raise PermissionDenied("Access denied")
row = rdata(w.csvfilename)
success = row.use_impellerdata()
@@ -481,12 +479,10 @@ def otw_use_impeller(request,id=0):
# Switch from Impeller to GPS (only for SpeedCoach 2, if impeller data)
-@login_required
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def otw_use_gps(request,id=0):
w = get_workout(id)
- if (checkworkoutuser(request.user,w)==False):
- raise PermissionDenied("Access denied")
row = rdata(w.csvfilename)
success = row.use_gpsdata()
@@ -748,20 +744,13 @@ def fitness_metric_view(request,mode='rower',days=42):
return HttpResponse("job queued")
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality",
redirect_field_name=None)
def workout_update_cp_view(request,id=0):
row = get_workout(id)
- if (checkworkoutuser(request.user,row)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
-
- return HttpResponseRedirect(url)
-
row.rankingpiece = True
row.save()
@@ -778,17 +767,10 @@ def workout_update_cp_view(request,id=0):
# Reload the workout and calculate the summary from the stroke data (lapIDx)
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_recalcsummary_view(request,id=0):
row = get_workout(id)
- if (checkworkoutuser(request.user,row)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
-
- return HttpResponseRedirect(url)
-
filename = row.csvfilename
rowdata = rdata(filename)
if rowdata:
@@ -1359,7 +1341,7 @@ def team_comparison_select(request,
if id:
firstworkout = get_workout(id)
- if not checkworkoutuserview(request.user,firstworkout):
+ if not is_workout_user(request.user,firstworkout):
raise PermissionDenied("You are not allowed to use this workout")
firstworkoutquery = Workout.objects.filter(id=encoder.decode_hex(id))
@@ -1791,7 +1773,7 @@ def workouts_view(request,message='',successmessage='',
# check if access is allowed
- if not checkviewworkouts(request.user,r):
+ if not is_rower_team_member(request.user,r):
raise PermissionDenied("Access denied")
@@ -2104,6 +2086,7 @@ def workout_fusion_list(request,id=0,message='',successmessage='',
})
# Basic view of workout
+@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_view(request,id=0):
request.session['referer'] = absolute(request)['PATH']
@@ -2112,19 +2095,13 @@ def workout_view(request,id=0):
else:
rower = None
- try:
- row = Workout.objects.get(id=encoder.decode_hex(id))
- except Workout.DoesNotExist:
- raise Http404("Workout doesn't exist")
+ row = get_workout_by_opaqueid(request,id)
comments = WorkoutComment.objects.filter(workout=row)
aantalcomments = len(comments)
- if row.privacy == 'private' and not checkworkoutuser(request.user,row):
- raise PermissionDenied("Access denied")
-
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
for i in g:
try:
@@ -2204,6 +2181,7 @@ def workout_view(request,id=0):
# Resets stroke data to raw data (pace)
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality",
redirect_field_name=None)
@@ -2213,13 +2191,6 @@ def workout_undo_smoothenpace_view(
row = get_workout(id)
r = getrower(request.user)
- if (checkworkoutuser(request.user,row)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
-
- return HttpResponseRedirect(url)
-
filename = row.csvfilename
row = rdata(filename)
if row == 0:
@@ -2243,6 +2214,7 @@ def workout_undo_smoothenpace_view(
# Data smoothing of pace data
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality",
redirect_field_name=None)
@@ -2253,12 +2225,6 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""):
r = getrower(request.user)
- if (checkworkoutuser(request.user,row)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
-
- return HttpResponseRedirect(url)
filename = row.csvfilename
row = rdata(filename)
@@ -2373,6 +2339,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""):
'id':row.id})
# Get weather for given location and date/time
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
redirect_field_name=None)
@@ -2382,12 +2349,6 @@ def workout_downloadwind_view(request,id=0,
row = get_workout(id)
f1 = row.csvfilename
- if (checkworkoutuser(request.user,row)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
-
- return HttpResponseRedirect(url)
# create bearing
rowdata = rdata(f1)
@@ -2448,6 +2409,7 @@ def workout_downloadwind_view(request,id=0,
return response
# Get weather for given location and date/time
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None)
def workout_downloadmetar_view(request,id=0,
airportcode=None,
@@ -2455,12 +2417,7 @@ def workout_downloadmetar_view(request,id=0,
row = get_workout(id)
f1 = row.csvfilename
- if (checkworkoutuser(request.user,row)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
- return HttpResponseRedirect(url)
# create bearing
rowdata = rdata(f1)
@@ -2523,6 +2480,7 @@ def workout_downloadmetar_view(request,id=0,
# Show form to update wind data
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None)
def workout_wind_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
@@ -2543,13 +2501,6 @@ def workout_wind_view(request,id=0,message="",successmessage=""):
]
- if (checkworkoutuser(request.user,row)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
-
- return HttpResponseRedirect(url)
-
# get data
f1 = row.csvfilename
@@ -2659,17 +2610,13 @@ def workout_wind_view(request,id=0,message="",successmessage=""):
# Show form to update River stream data (for river dwellers)
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None)
def workout_stream_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
r = getrower(request.user)
- if (checkworkoutuser(request.user,row)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
- return HttpResponseRedirect(url)
# create interactive plot
@@ -2745,23 +2692,15 @@ def workout_stream_view(request,id=0,message="",successmessage=""):
'the_div':div})
# Form to set average crew weight and boat type, then run power calcs
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember, login_url="/rowers/paidplans",redirect_field_name=None)
def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
w = get_workout(id)
r = getrower(request.user)
- mayedit = 0
- if request.user == w.user.user:
- mayedit=1
- if checkworkoutuser(request.user,w):
- mayedit=1
+ mayedit = 1
- if (checkworkoutuser(request.user,w)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
- return HttpResponseRedirect(url)
if request.method == 'POST':
@@ -2879,15 +2818,11 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
'form':form,
})
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def instroke_view(request,id=0):
w = get_workout(id)
r = getrower(request.user)
- mayedit = 0
- if request.user == w.user.user:
- mayedit=1
- if checkworkoutuser(request.user,w):
- mayedit=1
+ mayedit = 1
breadcrumbs = [
{
@@ -2909,12 +2844,7 @@ def instroke_view(request,id=0):
g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime")
# check if user is owner of this workout
- if (checkworkoutuser(request.user,w)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
- return HttpResponseRedirect(url)
rowdata = rrdata(csvfile=w.csvfilename)
try:
@@ -2937,16 +2867,11 @@ def instroke_view(request,id=0):
# generate instroke chart
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def instroke_chart(request,id=0,metric=''):
w = get_workout(id)
- if (checkworkoutuser(request.user,w)==False):
- message = "You are not allowed to edit this workout"
- messages.error(request,message)
- url = reverse('workouts_view')
- return HttpResponseRedirect(url)
rowdata = rrdata(csvfile=w.csvfilename)
instrokemetrics = rowdata.get_instroke_columns()
@@ -2995,14 +2920,12 @@ def instroke_chart(request,id=0,metric=''):
# data explorer
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_data_view(request, id=0):
r = getrower(request.user)
w = get_workout(id)
- if not checkworkoutuser(request.user,w):
- raise PermissionDenied('Access Denied')
breadcrumbs = [
{
@@ -3100,7 +3023,7 @@ def workout_data_view(request, id=0):
# Stats page
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_stats_view(request,id=0,message="",successmessage=""):
r = getrower(request.user)
@@ -3109,7 +3032,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
mayedit = 0
if request.user == w.user.user:
mayedit=1
- if checkworkoutuser(request.user,w):
+ if is_workout_user(request.user,w):
mayedit=1
breadcrumbs = [
@@ -3135,8 +3058,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
# prepare data frame
datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id))
- if (checkworkoutuserview(request.user,row)==False):
- raise PermissionDenied('Access Denied')
+
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly,
ignoreadvanced=False)
@@ -3377,11 +3299,12 @@ def workout_workflow_config2_view(request,userid=0):
# Workflow View
@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_workflow_view(request,id):
request.session['referer'] = absolute(request)['PATH']
request.session['lastworkout'] = id
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
- row = get_workout_permittedview(request.user,id)
+ row = get_workout_by_opaqueid(request,id)
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
@@ -3463,7 +3386,7 @@ def workout_workflow_view(request,id):
})
# The famous flex chart
-@login_required()
+@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_flexchart3_view(request,*args,**kwargs):
try:
@@ -3487,12 +3410,12 @@ def workout_flexchart3_view(request,*args,**kwargs):
mayedit=0
if not request.user.is_anonymous:
r = getrower(request.user)
- result = request.user.is_authenticated and ispromember(request.user)
+ result = ispromember(request.user)
if result:
promember=1
if request.user == row.user.user:
mayedit=1
- if checkworkoutuser(request.user,row):
+ if is_workout_user(request.user,row):
mayedit=1
workouttype = 'ote'
@@ -3977,7 +3900,7 @@ def workout_comment_view(request,id=0):
# The basic edit page
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_edit_view(request,id=0,message="",successmessage=""):
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
request.session['referer'] = absolute(request)['PATH']
@@ -3986,8 +3909,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
- if (checkworkoutuser(request.user,row)==False):
- raise PermissionDenied("Access denied")
+
if request.user.rower.rowerplan == 'basic' and 'speedcoach2' in row.workoutsource:
data = getsmallrowdata_db(['wash'],ids=[encoder.decode_hex(id)])
@@ -4293,7 +4215,7 @@ def workout_map_view(request,id=0):
# Image upload
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_uploadimage_view(request,id):
is_ajax = False
if request.is_ajax():
@@ -4319,8 +4241,7 @@ def workout_uploadimage_view(request,id):
]
- if not checkworkoutuser(request.user,w):
- raise PermissionDenied("You are not allowed to edit this workout")
+
images = GraphImage.objects.filter(workout=w)
@@ -4398,7 +4319,7 @@ def workout_uploadimage_view(request,id):
# Generic chart creation
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_add_chart_view(request,id,plotnr=1):
w = get_workout(id)
@@ -4406,26 +4327,24 @@ def workout_add_chart_view(request,id,plotnr=1):
plotnr = int(plotnr)
- if (checkworkoutuser(request.user,w)==False):
- raise PermissionDenied("You are not allowed add plots to this workout")
+
+ f1 = w.csvfilename[6:-4]
+ timestr = strftime("%Y%m%d-%H%M%S")
+ imagename = f1+timestr+'.png'
+ u = w.user.user
+ r = getrower(u)
+ title = w.name
+ res,jobid = uploads.make_plot(
+ r,w,f1,w.csvfilename,'timeplot',title,plotnr=plotnr,
+ imagename=imagename
+ )
+ if res == 0:
+ messages.error(request,jobid)
else:
- f1 = w.csvfilename[6:-4]
- timestr = strftime("%Y%m%d-%H%M%S")
- imagename = f1+timestr+'.png'
- u = w.user.user
- r = getrower(u)
- title = w.name
- res,jobid = uploads.make_plot(
- r,w,f1,w.csvfilename,'timeplot',title,plotnr=plotnr,
- imagename=imagename
- )
- if res == 0:
- messages.error(request,jobid)
- else:
- try:
- request.session['async_tasks'] += [(jobid,'make_plot')]
- except KeyError:
- request.session['async_tasks'] = [(jobid,'make_plot')]
+ try:
+ request.session['async_tasks'] += [(jobid,'make_plot')]
+ except KeyError:
+ request.session['async_tasks'] = [(jobid,'make_plot')]
url = reverse(r.defaultlandingpage,kwargs={'id':encoder.encode_hex(w.id)})
@@ -4437,12 +4356,13 @@ def workout_add_chart_view(request,id,plotnr=1):
@login_required
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_toggle_ranking(request,id=0):
is_ajax = False
if request.is_ajax():
is_ajax = True
- row = get_workout_permitted(request.user,id)
+ row = get_workout_by_opaqueid(request,id)
row.rankingpiece = not row.rankingpiece
row.save()
@@ -5237,9 +5157,9 @@ def graph_show_view(request,id):
raise Http404("This workout doesn't exist")
# Restore original stroke data and summary
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_summary_restore_view(request,id,message="",successmessage=""):
- row = get_workout_permitted(request.user,id)
+ row = get_workout_by_opaqueid(request,id)
s = ""
# still here - this is a workout we may edit
@@ -5287,11 +5207,12 @@ def workout_summary_restore_view(request,id,message="",successmessage=""):
return HttpResponseRedirect(url)
# Split a workout
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
redirect_field_name=None)
def workout_split_view(request,id=0):
- row = get_workout_permitted(request.user,id)
+ row = get_workout_by_opaqueid(request,id)
r = row.user
@@ -5398,8 +5319,8 @@ def workout_fusion_view(request,id1=0,id2=1):
w1 = Workout.objects.get(id=id1)
w2 = Workout.objects.get(id=id2)
r = w1.user
- if (checkworkoutuser(request.user,w1)==False) or \
- (checkworkoutuser(request.user,w2)==False):
+ if (is_workout_user(request.user,w1)==False) or \
+ (is_workout_user(request.user,w2)==False):
raise PermissionDenied("You are not allowed to use these workouts")
except Workout.DoesNotExist:
raise Http404("One of the workouts doesn't exist")
@@ -5475,10 +5396,10 @@ def workout_fusion_view(request,id1=0,id2=1):
# Edit the splits/summary
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_summary_edit_view(request,id,message="",successmessage=""
):
- row = get_workout_permitted(request.user,id)
+ row = get_workout_by_opaqueid(request,id)
r = getrower(request.user)
breadcrumbs = [
{
@@ -6024,10 +5945,11 @@ def workout_code_delete_view(request,id=0):
return HttpResponseRedirect(url)
-class WorkoutDelete(DeleteView):
+class WorkoutDelete(PermissionRequiredMixin,DeleteView):
login_required = True
model = Workout
template_name = 'workout_delete_confirm.html'
+ permission_required = 'workout.change_workout'
# extra parameters
def get_context_data(self, **kwargs):
@@ -6082,7 +6004,6 @@ class WorkoutDelete(DeleteView):
except Workout.DoesNotExist:
raise Http404("One of the workouts doesn't exist")
# obj = super(WorkoutDelete, self).get_object(*args, **kwargs)
- if not checkaccessuser(self.request.user,obj.user):
- raise PermissionDenied('You are not allowed to delete this workout')
+
return obj