diff --git a/008fc633-d9d4-4abc-9812-e22014e19498.tcx b/008fc633-d9d4-4abc-9812-e22014e19498.tcx
deleted file mode 100644
index 31d671d7..00000000
--- a/008fc633-d9d4-4abc-9812-e22014e19498.tcx
+++ /dev/null
@@ -1,2523 +0,0 @@
-
-
-
-
- 2016-05-20T13:41:26.962390+00:00
-
- 537
- 2000
- 1
-
- 148
-
-
- 156
-
- Active
- 21
- Manual
-
-
- <Element 'Notes' at 0x7f5eb3c3ef98>
-
-
-
- rowsandall.com/rowingdata
-
-
- rowingdata
-
-
- 0
- 75
-
- Release
-
- EN
- 000-00000-00
-
-
diff --git a/03f8abfb-ccec-45bf-ac29-b193477d2045.tcx b/03f8abfb-ccec-45bf-ac29-b193477d2045.tcx
deleted file mode 100644
index eb0a1c3e..00000000
--- a/03f8abfb-ccec-45bf-ac29-b193477d2045.tcx
+++ /dev/null
@@ -1,2523 +0,0 @@
-
-
-
-
- 2016-05-20T13:41:26.962390+00:00
-
- 537
- 2000
- 1
-
- 148
-
-
- 156
-
- Active
- 21
- Manual
-
-
- <Element 'Notes' at 0x13b50a20>
-
-
-
- rowsandall.com/rowingdata
-
-
- rowingdata
-
-
- 0
- 75
-
- Release
-
- EN
- 000-00000-00
-
-
diff --git a/b90680ee-35d5-4633-9974-fdc4bba4c68f.tcx b/b90680ee-35d5-4633-9974-fdc4bba4c68f.tcx
deleted file mode 100644
index 97ac8d4f..00000000
--- a/b90680ee-35d5-4633-9974-fdc4bba4c68f.tcx
+++ /dev/null
@@ -1,2523 +0,0 @@
-
-
-
-
- 2016-05-20T13:41:26.962390+00:00
-
- 537
- 2000
- 1
-
- 148
-
-
- 156
-
- Active
- 21
- Manual
-
-
- <Element 'Notes' at 0x000002C71C48FE58>
-
-
-
- rowsandall.com/rowingdata
-
-
- rowingdata
-
-
- 0
- 75
-
- Release
-
- EN
- 000-00000-00
-
-
diff --git a/c6c032ca-da24-42cf-b61f-02816b7e2ccd.tcx b/c6c032ca-da24-42cf-b61f-02816b7e2ccd.tcx
deleted file mode 100644
index 98408a5a..00000000
--- a/c6c032ca-da24-42cf-b61f-02816b7e2ccd.tcx
+++ /dev/null
@@ -1,2523 +0,0 @@
-
-
-
-
- 2016-05-20T13:41:26.962390+00:00
-
- 537
- 2000
- 1
-
- 148
-
-
- 156
-
- Active
- 21
- Manual
-
-
- <Element 'Notes' at 0x15038e80>
-
-
-
- rowsandall.com/rowingdata
-
-
- rowingdata
-
-
- 0
- 75
-
- Release
-
- EN
- 000-00000-00
-
-
diff --git a/requirements.txt b/requirements.txt
index 707dd8b5..8f8eb580 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -174,7 +174,7 @@ ratelim==0.1.6
redis==3.2.1
requests==2.21.0
requests-oauthlib==1.2.0
-rowingdata==2.6.4
+rowingdata==2.6.7
rowingphysics==0.5.0
rq==0.13.0
rules==2.1
diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py
index 4a082f35..83d18ae7 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,ispromember
from iso8601 import ParseError
import numpy
@@ -155,7 +156,7 @@ def add_stroke_data(user,c2id,workoutid,startdatetime,csvfilename,
def get_c2_workouts(rower):
- if not isprorower(rower):
+ if not ispromember(rower.user):
return 0
try:
@@ -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/dataprepnodjango.py b/rowers/dataprepnodjango.py
index f41498fb..15f66293 100644
--- a/rowers/dataprepnodjango.py
+++ b/rowers/dataprepnodjango.py
@@ -140,10 +140,10 @@ def timedeltaconv(x):
def rdata(file,rower=rrower()):
try:
- res = rrdata(file,rower=rower)
+ res = rrdata(csvfile=file,rower=rower)
except IOError:
try:
- res = rrdata(file+'.gz',rower=rower)
+ res = rrdata(csvfile=file+'.gz',rower=rower)
except IOError:
res = 0
@@ -948,7 +948,7 @@ def smalldataprep(therows,xparam,yparam1,yparam2):
f1 = workout.csvfilename
try:
- rowdata = dataprep(rrdata(f1).df)
+ rowdata = dataprep(rrdata(csvfile=f1).df)
rowdata = pd.DataFrame({xparam: rowdata[xparam],
yparam1: rowdata[yparam1],
@@ -960,7 +960,7 @@ def smalldataprep(therows,xparam,yparam1,yparam2):
df = pd.concat([df,rowdata],ignore_index=True)
except IOError:
try:
- rowdata = dataprep(rrdata(f1+'.gz').df)
+ rowdata = dataprep(rrdata(csvfile=f1+'.gz').df)
rowdata = pd.DataFrame({xparam: rowdata[xparam],
yparam1: rowdata[yparam1],
yparam2: rowdata[yparam2],
diff --git a/rowers/decorators.py b/rowers/decorators.py
index f9d03ed7..78dc7af6 100644
--- a/rowers/decorators.py
+++ b/rowers/decorators.py
@@ -4,6 +4,7 @@ from __future__ import print_function
from __future__ import unicode_literals
from django.contrib.auth.decorators import login_required,user_passes_test
+from django.urls import reverse
from django.http import HttpResponseRedirect
from django.core.exceptions import PermissionDenied
@@ -29,6 +30,9 @@ def user_passes_test(test_func, message=default_message,login_url=None,redirect_
def decorator(view_func):
@wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
+ if request.user.is_anonymous:
+ url = reverse('login')+'?next='+request.path
+ return HttpResponseRedirect(url)
if not test_func(request.user):
messages.error(request, message)
return HttpResponseRedirect(login_url)
@@ -48,4 +52,3 @@ def login_required_message(function=None, message=default_message):
if function:
return actual_decorator(function)
return actual_decorator
-
diff --git a/rowers/forms.py b/rowers/forms.py
index 20882308..530c842d 100644
--- a/rowers/forms.py
+++ b/rowers/forms.py
@@ -1404,6 +1404,7 @@ class FlexAxesForm(forms.Form):
extrametrics = kwargs.pop('extrametrics',[])
super(FlexAxesForm, self).__init__(*args, **kwargs)
+
rower = Rower.objects.get(user=request.user)
axchoicespro = (
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/interactiveplots.py b/rowers/interactiveplots.py
index 428c1968..73c72151 100644
--- a/rowers/interactiveplots.py
+++ b/rowers/interactiveplots.py
@@ -2680,7 +2680,10 @@ def interactive_windchart(id=0,promember=0):
if rowdata == 0:
return 0
- dist = rowdata.df.loc[:,'cum_dist']
+ try:
+ dist = rowdata.df.loc[:,'cum_dist']
+ except KeyError:
+ return ['','No Data Found']
try:
vwind = rowdata.df.loc[:,'vwind']
@@ -2770,7 +2773,10 @@ def interactive_streamchart(id=0,promember=0):
if rowdata == 0:
return "","No Valid Data Available"
- dist = rowdata.df.loc[:,'cum_dist']
+ try:
+ dist = rowdata.df.loc[:,'cum_dist']
+ except KeyError:
+ return ['','No Data found']
try:
vstream = rowdata.df.loc[:,'vstream']
diff --git a/rowers/mailprocessing.py b/rowers/mailprocessing.py
index 37017602..fb275a11 100644
--- a/rowers/mailprocessing.py
+++ b/rowers/mailprocessing.py
@@ -64,18 +64,18 @@ def send_confirm(user, name, link, options):
def rdata(file, rower=rrower()):
""" Reads rowingdata data or returns 0 on Error """
try:
- result = rrdata(file, rower=rower)
+ result = rrdata(csvfile=file, rower=rower)
except IOError:
try:
- result = rrdata(file + '.gz', rower=rower)
+ result = rrdata(csvfile=file + '.gz', rower=rower)
except IOError:
result = 0
except TypeError:
try:
- result = rrdata(file)
+ result = rrdata(csvfile=file)
except IOError:
try:
- result = rrdata(file+'.gz', rower=rower)
+ result = rrdata(csvfile=file+'.gz', rower=rower)
except IOError:
result = 0
diff --git a/rowers/models.py b/rowers/models.py
index 01f49265..e412de4f 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -39,6 +39,10 @@ import pandas as pd
from dateutil import parser
import datetime
+#from rules.contrib.models import RulesModel
+
+from rowers.rower_rules import *
+
from rowers.rows import validate_file_extension
from collections import OrderedDict
from timezonefinder import TimezoneFinder
@@ -53,6 +57,10 @@ from rowsandall_app.settings import (
TWEET_CONSUMER_SECRET,
)
+
+
+# END PERMISSIONS
+
tweetapi = twitter.Api(consumer_key=TWEET_CONSUMER_KEY,
consumer_secret=TWEET_CONSUMER_SECRET,
access_token_key=TWEET_ACCESS_TOKEN_KEY,
@@ -322,14 +330,7 @@ class C2WorldClassAgePerformance(models.Model):
return thestring
-def is_not_basic(user):
- if user.rower.rowerplan == 'basic':
- raise ValidationError(
- "Basic user cannot be team manager"
- )
-
-# For future Team functionality
@python_2_unicode_compatible
class Team(models.Model):
choices = (
@@ -344,30 +345,28 @@ class Team(models.Model):
name = models.CharField(max_length=150,unique=True,verbose_name='Team Name')
notes = models.CharField(blank=True,max_length=200,verbose_name='Team Purpose')
- manager = models.ForeignKey(User, null=True,on_delete=models.CASCADE) # validators=[is_not_basic])
+ manager = models.ForeignKey(User, null=True,on_delete=models.CASCADE)
private = models.CharField(max_length=30,choices=choices,default='open',
verbose_name='Team Type')
viewing = models.CharField(max_length=30,choices=viewchoices,default='allmembers',verbose_name='Sharing Behavior')
+
def __str__(self):
return self.name
def save(self, *args, **kwargs):
manager = self.manager
- if manager.rower.rowerplan == 'basic':
- if manager.rower.protrialexpires < datetime.date.today() and manager.rower.plantrialexpires < datetime.date.today():
+ if not can_have_teams(manager):
raise ValidationError(
"Basic user cannot be team manager"
)
- if manager.rower.rowerplan in ['plan','pro']:
- otherteams = Team.objects.filter(manager=manager)
- if otherteams.count() >= 1:
- raise ValidationError(
- "Pro and Self-Coach users cannot have more than one team"
- )
+ if not can_add_team(manager):
+ raise ValidationError(
+ "Pro and Self-Coach users cannot have more than one team"
+ )
super(Team, self).save(*args,**kwargs)
@@ -658,6 +657,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):
@@ -896,6 +898,20 @@ class Rower(models.Model):
super(Rower, self).save(*args, **kwargs)
+ def get_managed_teams(self):
+ return Team.objects.filter(manager=self.user)
+
+ def get_coaches(self):
+ coaches = []
+ for group in self.coachinggroups.all():
+ try:
+ coach = Rower.objects.get(mycoachgroup=group)
+ coaches.append(coach)
+ except Rower.DoesNotExist:
+ pass
+
+ return coaches
+
class DeactivateUserForm(forms.ModelForm):
class Meta:
model = User
@@ -925,18 +941,19 @@ class CoachOffer(models.Model):
from django.db.models.signals import m2m_changed
+
def check_teams_on_change(sender, **kwargs):
instance = kwargs.pop('instance', None)
action = kwargs.pop('action', None)
pk_set = kwargs.pop('pk_set',None)
- if action == 'pre_add' and instance.rowerplan=='basic':
- if instance.protrialexpires < datetime.date.today() and instance.plantrialexpires < datetime.date.today():
- for id in pk_set:
- team = Team.objects.get(id=id)
- if team.manager.rower.rowerplan not in ['coach']:
- raise ValidationError(
- "You cannot join a team led by a Pro, Free Coach Plan or Self-Coach user"
- )
+ if action == 'pre_add':
+ #if instance.protrialexpires < datetime.date.today() and instance.plantrialexpires < datetime.date.today():
+ for id in pk_set:
+ team = Team.objects.get(id=id)
+ if not can_join_team(instance.user,team):
+ raise ValidationError(
+ "You cannot join a team led by a Pro, Free Coach Plan or Self-Coach user"
+ )
m2m_changed.connect(check_teams_on_change, sender=Rower.team.through)
@@ -1165,95 +1182,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
@@ -1354,6 +1283,18 @@ class TrainingTarget(models.Model):
return stri
+def check_trainingtarget_on_change(sender,**kwargs):
+ instance = kwargs.pop('instance',None)
+ action = kwargs.pop('action',None)
+ pk_set = kwargs.pop('pk_set',None)
+ if action == 'pre_add':
+ for id in pk_set:
+ rower = Rower.objects.get(id=id)
+ if not can_plan_user(instance.manager.user,rower):
+ raise ValidationError("You cannot add this rower. Not your coachee")
+
+m2m_changed.connect(check_trainingtarget_on_change, sender=TrainingTarget.rowers.through)
+
class TrainingTargetForm(ModelForm):
class Meta:
model = TrainingTarget
@@ -1423,12 +1364,12 @@ class TrainingPlan(models.Model):
def save(self, *args, **kwargs):
manager = self.manager
+ if not can_add_plan(manager.user):
+ raise ValidationError(
+ "Basic user cannot have a training plan"
+ )
+
- if manager.rowerplan in ['basic','pro']:
- if manager.plantrialexpires < timezone.now().date():
- raise ValidationError(
- "Basic user cannot have a training plan"
- )
if self.enddate < self.startdate:
startdate = self.startdate
@@ -1439,6 +1380,13 @@ class TrainingPlan(models.Model):
if not self.enddate <= self.startdate:
super(TrainingPlan,self).save(*args, **kwargs)
+ # only add athletes that are allowed to be added
+ for rower in self.rowers.all():
+ if can_plan_user(manager.user,rower):
+ self.rowers.add(rower)
+ else:
+ self.rowers.remove(rower)
+
if self.status:
otherplans = TrainingPlan.objects.filter(
status=True).exclude(
@@ -1475,6 +1423,18 @@ class TrainingPlan(models.Model):
else:
createmacrofillers(self)
+def check_trainingplan_on_change(sender, **kwargs):
+ instance = kwargs.pop('instance',None)
+ action = kwargs.pop('action',None)
+ pk_set = kwargs.pop('pk_set',None)
+ if action == 'pre_add':
+ for id in pk_set:
+ rower = Rower.objects.get(id=id)
+ if not can_plan_user(instance.manager.user,rower):
+ raise ValidationError("You cannot add this rower. Not your coachee")
+
+m2m_changed.connect(check_trainingplan_on_change, sender=TrainingPlan.rowers.through)
+
class TrainingPlanForm(ModelForm):
class Meta:
@@ -2158,11 +2118,10 @@ class PlannedSession(models.Model):
manager = self.manager
if self.sessiontype not in ['race','indoorrace']:
- if manager.rower.rowerplan in ['basic','pro']:
- if manager.rower.plantrialexpires < timezone.now().date():
- raise ValidationError(
- "You must be a Self-Coach user or higher to create a planned session"
- )
+ if not can_add_session(self.manager):
+ raise ValidationError(
+ "You must be a Self-Coach user or higher to create a planned session"
+ )
# sort units
@@ -2769,7 +2728,7 @@ class Workout(models.Model):
def save(self, *args, **kwargs):
user = self.user
- if self.user.rowerplan == 'freecoach':
+ if not can_add_workout(user.user):
raise forms.ValidationError("Free Coach User cannot have any workouts")
super(Workout, self).save(*args, **kwargs)
diff --git a/rowers/permissions.py b/rowers/permissions.py
index 2d32add8..4950f910 100644
--- a/rowers/permissions.py
+++ b/rowers/permissions.py
@@ -22,7 +22,7 @@ class IsOwnerOrReadOnly(permissions.BasePermission):
return obj.user == request.user
class IsOwnerOrNot(permissions.BasePermission):
-
+
def has_object_permission(self, request, view, obj):
r = Rower.objects.get(user=request.user)
return (obj.user == r)
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
new file mode 100644
index 00000000..9a890920
--- /dev/null
+++ b/rowers/rower_rules.py
@@ -0,0 +1,708 @@
+import rules
+import datetime
+
+# PERMISSIONS
+
+# USER permissions
+
+"""
+USER permissions
+- There are 5 types of user: basic, pro, plan, coach, freecoach
+- These methods exist on user
+ - user.rower
+ - user.is_anonymous
+ - user.is_staff
+
+- These methods exist on rower
+ - rower.rowerplan
+ - rower.protrialexpires
+ - rower.plantrialexpires
+ - rower.mycoachgroup
+ - rower.coachinggroups
+ - rower.team
+
+- These Rules apply to Rowers
+ - Coach can have any number of groups
+ - test_permissions.PermissionFreeCoach.test_coach_groupmanager
+ - test_permissions.PermissionsBasicsTests.test_coach_groupmanager
+ - test_permissions.PermissionsViewTests.test_coach_groups_create
+ - Pro and Plan users can have one group and not more
+ - test_permissions.PermissionsFreeCoach.test_pro_groupmanager
+ - test_permissions.PermissionsViewTests.test_pro_groups_create
+ - Free Coach user cannot have workouts
+ - test_permissions.PermissionFreeCoach.test_add_workout_freecoach
+ - Free coach can create more than one group
+ - test_permissions.PermissionsFreeCoach.test_plan_groupmanager
+ - Free Coach & Plan (Self Coach) can create planned sessions and team planned sessions
+ - test_permissions.PermissionsFreeCoach.test_plan_create_session
+ - test_permissions.PermissionsFreeCoach.test_coach_create_session
+ - Pro cannot create planned sessions or team planned sessions
+ - test_permissions.PermissionsViewTests.test_pro_create_session
+ - Basic cannot join groups led by Free Coach
+ - test_permissions.PermissionFreeCoach.test_add_basic_pro_or_plan
+ - Coach can edit athlete settings (if in coachinggroup)
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_prefs
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_prefs_not
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_settings
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_settings_not
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_account
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_account_not
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_exportsettings
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_exportsettings_not
+ - Coach can upload a workout on behalf of athlete
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload_not
+ - Pro and Plan cannot upload a workout on behalf of athlete
+ - test_permissions.PermissionsViewTests.test_plan_edit_athlete_upload
+ - Coach can run analytics for athlete
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_analysis
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_analysis_not
+ - test_permissions.PermissionsViewTests.test_pro_edit_athlete_analysis
+ - Pro and Plan user cannot run analysis for members of their groups
+ - test_permissions.PermissionsViewTests.test_plan_edit_athlete_analysis
+ - Pro and Plan user cannot edit athlete sessings
+ - test_permissions.PermissionsViewTests.test_plan_edit_athlete_settings
+ - test_permissions.PermissionsViewTests.test_pro_edit_athlete_settings
+ - Self-Coach and Pro cannot create more than one group
+ - test_permissions.PermissionsFreeCoach.test_plan_groupmanager
+ - Pro users and higher can join group led by other Pro or higher user
+ - test_permissions.PermissionsFreeCoach.test_add_proplan_pro_or_plan
+ - Pro or basic cannot create planned sessions or team planned sessions
+ - test_permissions.PermissionsFreeCoach.test_pro_create_plannedsession
+
+"""
+
+# used in can_plan_user
+@rules.predicate
+def user_is_not_basic(user):
+ if user.rower.rowerplan != 'basic':
+ return True
+
+ if user.rower.protrialexpires >= datetime.date.today() and user.rower.plantrialexpires >= datetime.date.today():
+ return True
+
+ return False
+
+@rules.predicate
+def can_start_trial(user):
+ if user.is_anonymous:
+ return False
+
+
+ return user.rower.protrialexpires == datetime.date(1970,1,1)
+
+@rules.predicate
+def can_start_plantrial(user):
+ if user.is_anonymous:
+ return False
+
+ return user.rower.plantrialexpires == datetime.date(1970,1,1)
+
+@rules.predicate
+def is_staff(user):
+ return user.is_staff
+
+@rules.predicate
+def is_coach(user):
+ return user.rower.rowerplan in ['coach','freecoach']
+
+def is_paid_coach(user):
+ return user.rower.rowerplan == 'coach'
+
+@rules.predicate
+def is_planmember(user):
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ return r.rowerplan in ['coach','plan'] # freecoach?
+
+@rules.predicate
+def is_promember(user):
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ return r.rowerplan in ['pro','coach','plan'] # freecoach?
+
+@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
+
+can_have_teams = ispromember | is_coach
+
+@rules.predicate
+def can_add_team(user):
+ if is_coach(user):
+ return True
+
+ if ispromember(user):
+ otherteams = user.rower.get_managed_teams()
+ if otherteams.count() == 0:
+ return True
+
+ return False
+
+@rules.predicate
+def can_add_plan(user):
+ return isplanmember(user) or is_coach(user)
+
+@rules.predicate
+def can_add_workout(user):
+ if user.is_anonymous:
+ return False
+
+ return user.rower.rowerplan != 'freecoach'
+
+@rules.predicate
+def is_plantrial(user):
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ if r.rowerplan in ['basic','pro']:
+ return r.plantrialexpires >= datetime.date.today()
+ if r.rowerplan == 'freecoach':
+ if r.mycoachgroup is not None:
+ return len(r.mycoachgroup)>=4
+
+ return False
+
+
+isplanmember = is_planmember | is_plantrial
+
+@rules.predicate
+def can_add_session(user):
+ return isplanmember(user) or is_coach(user)
+
+# User / Coach relationships (Rower object)
+
+@rules.predicate
+def can_plan(user):
+ return user.rower.rowerplan in ['plan','coach','freecoach']
+
+# checks if rower is coach of user (or is user himself)
+@rules.predicate
+def is_coach_user(usercoach,userrower):
+ if usercoach == userrower:
+ return True
+
+ if not is_coach(usercoach):
+ return False
+
+ r = userrower.rower
+
+ coaches = []
+
+ for group in r.coachinggroups.all():
+ newcoaches = group.get_coaches()
+ for coach in newcoaches:
+ coaches.append(coach)
+
+ for coach in coaches:
+ if usercoach.rower == coach:
+ return True
+
+ return False
+
+# checks if rower is coach of user (or is user himself)
+@rules.predicate
+def is_anonymous_or_coach(usercoach,userrower):
+ print(usercoach,userrower)
+ if usercoach == userrower:
+ return True
+
+ if userrower.is_anonymous and userrower.is_anonymous:
+ return True
+
+ if not is_coach(usercoach):
+ return False
+
+ r = userrower.rower
+
+ coaches = []
+
+ for group in r.coachinggroups.all():
+ newcoaches = group.get_coaches()
+ for coach in newcoaches:
+ coaches.append(coach)
+
+ for coach in coaches:
+ if usercoach.rower == coach:
+ return True
+
+ return False
+
+# check if rower and user are members of the same team
+@rules.predicate
+def is_rower_team_member(user,rower):
+ if user.rower == rower:
+ return True
+
+ teams = 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_add_workout_member(user,rower):
+ if not user:
+ return False
+ if user.is_anonymous:
+ return False
+ if user == rower.user:
+ return True
+ # only below tested - need test user == rower.user
+ return is_coach(user) and user.rower in rower.get_coaches()
+
+# check if user can plan for the rower
+@rules.predicate
+def can_plan_user(user,rower):
+ # user must have planning permission
+ if not can_plan(user):
+ return False
+
+ # if has planning permission, can always plan for himself
+ if rower == user.rower:
+ return True
+
+ teams = user.rower.get_managed_teams()
+ # free coach, plan etc cannot plan for basic
+ if not is_paid_coach(user) and user_is_not_basic(user):
+ for t in teams:
+ if rower in t.rower.all():
+ return True
+
+ # paying coach can plan for all kinds of rowers
+ if is_paid_coach(user):
+ for t in teams:
+ if rower in t.rower.all():
+ return True
+
+ return False
+
+rules.add_perm('rower.add_plan',can_plan_user) # replaces checkaccessplanuser
+rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser
+rules.add_perm('rower.is_pro',ispromember)
+rules.add_perm('rower.is_staff',is_staff)
+
+# WORKOUT permissions
+
+"""
+WORKOUT permissions
+- These methods exist on Workout
+ - workout.privacy
+
+- These rules apply to workouts
+ - User can add, delete and change their own workouts
+ - test_aworkouts
+ - Coach can add and change workouts for their athletes, but not delete
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload_not
+ - Basic, Pro and Plan user can view but cannot add or change an athlete's workout
+ - test_permissions.PermissionsViewTests.test_coach_edit_athlete_workout
+ - test_permissions.PermissionsViewTests.test_plan_edit_athlete_upload
+ - test_permissions.PermissionsViewTests.test_plan_edit_athlete_workout
+ - test_permissions.PermissionsViewTests.test_basic_edit_athelte_workout
+ - Anonymous users can view team members' workout (but not see list of workouts)
+ - test_aworkouts
+
+ - Rules for Workouts are transferred to objects related to the Workout,
+ - Charts
+ - Video Analysis
+ - WorkoutComment?
+
+"""
+
+# check if user is owner or coach of owner of workout
+@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
+
+ return is_coach_user(user,workout.user.user)
+
+# check if user is in same team as owner of workout
+@rules.predicate
+def is_workout_team(user,workout):
+ if user.is_anonymous:
+ return False
+
+ try:
+ r = user.rower
+ except AttributeError:
+ return False
+
+ if workout.user == r:
+ return True
+
+ return is_rower_team_member(user,workout.user)
+
+# check if user can see workout
+@rules.predicate
+def can_view_workout(user,workout):
+ if workout.privacy != 'private':
+ return True
+ return user.rower == workout.user
+
+can_change_workout = is_workout_user
+
+rules.add_perm('workout.change_workout',can_change_workout) # replaces checkworkoutuser
+rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutuserview
+
+
+
+# checkviewworkouts
+
+# PLANNING permissions
+
+"""
+- These rules apply to planning
+ - Free coach can create planned sessions and team planned sessions
+ - test_permissions.PermissionsViewTests.test_coach_create_session
+ - Self coach and higher can create planned sessions and team planned sessions
+ - test_permissions.PermissionsFreeCoach.test_plan_create_session
+ - test_permissions.PermissionsFreeCoach.test_coach_create_session
+ - Coach can create planned sessions and team planned sessions
+ - test_permissions.PermissionsFreeCoach.test_plan_create_session
+ - test_permissions.PermissionsFreeCoach.test_coach_create_session
+ - Self Coach and higher can create planned sessions and team planned sessions
+ - test_permissions.PermissionsViewTests.test_plan_create_session
+ - Pro or Basic cannot create planned sessions or team planned sessions
+ - test_permissions.PermissionsFreeCoach.test_pro_create_plannedsession
+ - test_permissions.PermissionsFreeCoach.test_basic_create_plannedsession
+ - WHO can comment (plannedsession_comment_view)
+ - Only Session manager can change session
+ - Strict View rules (stricter than workouts)
+ - Basic user cannot manage a training plan
+
+ - TrainingTarget
+ - rules for view, add, change, delete
+ - TrainingPlan
+ - rules for view, add, change, delete
+ - Cycle
+ - inherits rules from TrainingPlan
+ - PlannedSession
+ - rules for view, add, change, delete, clone, save as template
+ - check team
+
+ - Special rules for Race (cannot be edited ex post)
+
+"""
+
+# Training Target rules
+
+# untested can_view_target to can_delete_target
+@rules.predicate
+def can_view_target(user,target):
+ if user.is_anonymous:
+ return False
+ if user == target.manager.user:
+ return True
+
+ # a target's coach can view as well
+ if is_coach_user(user,target.manager.user):
+ return True
+
+ # the object can view as well
+ if user.rower in target.rowers.all():
+ return True
+
+@rules.predicate
+def can_change_target(user,target):
+ if user.is_anonymous:
+ return False
+ return user == target.manager.user
+
+@rules.predicate
+def can_delete_target(user,target):
+ if user.is_anonymous:
+ return False
+ return user == target.manager.user
+
+rules.add_perm('target.view_target',can_view_target)
+rules.add_perm('target.change_target',can_change_target)
+rules.add_perm('target.delete_target',can_delete_target)
+
+@rules.predicate
+def can_view_plan(user,plan):
+ if user.is_anonymous:
+ return False
+ if user == plan.manager.user:
+ return True
+
+ # a plan's coach can view as well
+ # below untested
+ if is_coach_user(user,plan.manager.user):
+ return True
+
+ # the object can view as well
+ if user.rower in plan.rowers.all():
+ return True
+
+@rules.predicate
+def can_change_plan(user,plan):
+ if user.is_anonymous:
+ return False
+ return user == plan.manager.user
+
+# below untested
+@rules.predicate
+def can_delete_plan(user,plan):
+ if user.is_anonymous:
+ return False
+ return user == plan.manager.user
+
+
+
+rules.add_perm('plan.view_plan',can_view_plan)
+rules.add_perm('plan.change_plan',can_change_plan)
+rules.add_perm('plan.delete_plan',can_delete_plan)
+rules.add_perm('plan.can_add_plan',can_add_plan)
+
+# untested
+@rules.predicate
+def can_view_cycle(user,cycle):
+ try:
+ return can_view_cycle(user,cycle.plan)
+ except AttributeError:
+ return can_view_plan(user,cycle.plan)
+
+ return False
+
+@rules.predicate
+def can_change_cycle(user,cycle):
+ try:
+ return can_change_cycle(user,cycle.plan)
+ except AttributeError:
+ return can_change_plan(user,cycle.plan)
+
+ return False
+
+@rules.predicate
+def can_delete_cycle(user,cycle):
+ try:
+ return can_delete_cycle(user,cycle.plan)
+ except AttributeError:
+ return can_delete_plan(user,cycle.plan)
+
+ return False
+
+
+rules.add_perm('cycle.view_cycle',can_view_cycle)
+rules.add_perm('cycle.change_cycle',can_change_cycle)
+rules.add_perm('cycle.delete_cycle',can_delete_cycle)
+
+
+# check if user has view access to session
+@rules.predicate
+def can_view_session(user,session):
+ if session.sessiontype in ['race','indoorrace']:
+ return True
+ if user.is_anonymous:
+ return False
+ # session manager can view session
+ if user == session.manager:
+ return True
+ # if you're a rower in the session you can view it
+ # below untested
+ if user.rower in session.rower.all():
+ return True
+ # coach users can view sessions created by their team members
+ # below untested
+ if is_coach(user):
+ teams = user.rower.get_managed_teams()
+ teamusers = [member.u for member in t.rower.all()]
+ if session.manager in teamusers:
+ return True
+
+ return False
+
+@rules.predicate
+def can_change_session(user,session):
+ if user.is_anonymous:
+ return False
+ # session part of a race should not be changed through the session interface
+ if session.sessiontype in ['race','indoorrace']:
+ return False
+ if user == session.manager:
+ return True
+
+ return False
+
+@rules.predicate
+def can_delete_session(user,session):
+ if user.is_anonymous:
+ return False
+
+ if session.sessiontype in ['race','indoorrace']:
+ return False
+
+ if user == session.manager:
+ return True
+
+ return False
+
+
+
+rules.add_perm('plannedsession.add_session',can_add_session)
+rules.add_perm('plannedsession.view_session',can_view_session)
+rules.add_perm('plannedsession.change_session',can_change_session)
+rules.add_perm('plannedsession.delete_session',can_delete_session)
+
+# TEAM (group) permissions
+
+"""
+- These methods exist on team
+ - team.manager
+ - team.private
+
+- These rules apply to a team
+ - A Pro or Plan rower can be manager of only 1 team
+ - test_permissions.PermissionsFreeCoach.test_pro_groupmanager
+ - test_permissions.PermissionsViewTests.test_pro_groups_create
+ - A coach can have any number of teams (as manager)
+ - test_permissions.PermissionsBasicTest.test_coach_groupmanager
+ - Basic user cannot manage a group
+ - test_permissions.PermissionsFreeCoach.test_basic_groupmanager
+ - Basic user cannot join groups led by Free Coach or Pro
+ - test_permissions.PermissionFreeCoach.test_add_basic_pro_or_plan
+ - Basic athletes can be member of Coach led group
+ - test_permissions.PermissionsBasicTest.test_add_coach
+ - Pro users (and higher) can join team led by other Pro (or higher) user
+ - test_permissions.PermissionsFreeCoach.test_add_proplan_pro_or_plan
+ - test_permissions.PermissionsViewTests.test_team_member_request_pro_pro
+ - test_permissions.PermissionsViewTests.test_team_member_request_basic_pro
+ - On downgrade, Coach users lose all but their oldest team (add test!)
+
+"""
+
+# check if user is manager of the team
+@rules.predicate
+def is_team_manager(user,team):
+ return team.manager == user
+
+# check is user is member of team - untested
+@rules.predicate
+def is_team_member(user,team):
+ members = team.rower.all()
+ return user in [member.user for member in members]
+
+# check if user can view team
+@rules.predicate
+def can_view_team(user,team):
+ # user based - below untested
+ if user.rower.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach':
+ return False
+ # team is public
+ if team.private == 'open':
+ return True
+ # team is private - below untested
+ return is_team_member(user,team) | is_team_manager(user,team)
+
+@rules.predicate
+def can_change_team(user,team):
+ return is_team_manager(user,team)
+
+@rules.predicate
+def can_delete_team(user,team):
+ return is_team_manager(user,team)
+
+@rules.predicate
+def can_join_team(user,team):
+ return is_paid_coach(team.manager) or ispromember(user)
+
+# For Team functionality
+rules.add_perm('teams.view_team',can_view_team)
+rules.add_perm('teams.add_team',user_is_not_basic)
+rules.add_perm('teams.change_team',can_change_team)
+rules.add_perm('teams.delete_team',can_delete_team)
+
+# RACING permissions
+
+"""
+ - VirtualRace
+ - rules to add, view, delete, change
+ - GeoCourse
+ - rules to add, view, delete, change
+ - RaceLogo
+"""
+
+@rules.predicate
+def can_change_course(user,course):
+ if user.is_anonymous:
+ return False
+
+ return course.manager == user.rower
+
+# untested
+@rules.predicate
+def can_delete_course(user,course):
+ if user.is_anonymous:
+ return False
+
+ return course.manager == user.rower
+
+@rules.predicate
+def can_delete_logo(user,logo):
+ if user.is_anonymous:
+ return False
+
+ return logo.user == user
+
+@rules.predicate
+def can_change_race(user,race):
+ if user.is_anonymous:
+ return False
+
+ return race.manager == user
+
+# everybody can view or add a course
+rules.add_perm('course.change_course',can_change_course)
+rules.add_perm('course.delete_course',can_delete_course)
+
+rules.add_perm('racelogo.delete_logo',can_delete_logo)
+
+# everybody can view a race
+rules.add_perm('virtualevent.change_race',can_change_race)
+# can races be deleted?
+
+
+# ANALYSIS permissions
+
+"""
+ - Conditions
+ - Alerts
+ - rules to add, view, delete, change
+ - cpdata
+"""
diff --git a/rowers/rowing_workout_metrics_pb2.py b/rowers/rowing_workout_metrics_pb2.py
new file mode 100644
index 00000000..53d92d53
--- /dev/null
+++ b/rowers/rowing_workout_metrics_pb2.py
@@ -0,0 +1,203 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: rowing-workout-metrics.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='rowing-workout-metrics.proto',
+ package='rowing_workout_metrics',
+ syntax='proto3',
+ serialized_options=None,
+ serialized_pb=_b('\n\x1crowing-workout-metrics.proto\x12\x16rowing_workout_metrics\"p\n\x15WorkoutMetricsRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x0b\n\x03sex\x18\x02 \x01(\t\x12\x0b\n\x03\x66tp\x18\x03 \x01(\x01\x12\r\n\x05hrftp\x18\x04 \x01(\x01\x12\r\n\x05hrmax\x18\x05 \x01(\x01\x12\r\n\x05hrmin\x18\x06 \x01(\x01\"p\n\x16WorkoutMetricsResponse\x12\x0b\n\x03tss\x18\x01 \x01(\x01\x12\r\n\x05normp\x18\x02 \x01(\x01\x12\r\n\x05trimp\x18\x03 \x01(\x01\x12\r\n\x05hrtss\x18\x04 \x01(\x01\x12\r\n\x05normv\x18\x05 \x01(\x01\x12\r\n\x05normw\x18\x06 \x01(\x01\x32w\n\x07Metrics\x12l\n\x0b\x43\x61lcMetrics\x12-.rowing_workout_metrics.WorkoutMetricsRequest\x1a..rowing_workout_metrics.WorkoutMetricsResponseb\x06proto3')
+)
+
+
+
+
+_WORKOUTMETRICSREQUEST = _descriptor.Descriptor(
+ name='WorkoutMetricsRequest',
+ full_name='rowing_workout_metrics.WorkoutMetricsRequest',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='filename', full_name='rowing_workout_metrics.WorkoutMetricsRequest.filename', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='sex', full_name='rowing_workout_metrics.WorkoutMetricsRequest.sex', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='ftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.ftp', index=2,
+ number=3, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='hrftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrftp', index=3,
+ number=4, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='hrmax', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmax', index=4,
+ number=5, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='hrmin', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmin', index=5,
+ number=6, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=56,
+ serialized_end=168,
+)
+
+
+_WORKOUTMETRICSRESPONSE = _descriptor.Descriptor(
+ name='WorkoutMetricsResponse',
+ full_name='rowing_workout_metrics.WorkoutMetricsResponse',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='tss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.tss', index=0,
+ number=1, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='normp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normp', index=1,
+ number=2, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='trimp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.trimp', index=2,
+ number=3, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='hrtss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.hrtss', index=3,
+ number=4, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='normv', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normv', index=4,
+ number=5, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='normw', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normw', index=5,
+ number=6, type=1, cpp_type=5, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=170,
+ serialized_end=282,
+)
+
+DESCRIPTOR.message_types_by_name['WorkoutMetricsRequest'] = _WORKOUTMETRICSREQUEST
+DESCRIPTOR.message_types_by_name['WorkoutMetricsResponse'] = _WORKOUTMETRICSRESPONSE
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+WorkoutMetricsRequest = _reflection.GeneratedProtocolMessageType('WorkoutMetricsRequest', (_message.Message,), {
+ 'DESCRIPTOR' : _WORKOUTMETRICSREQUEST,
+ '__module__' : 'rowing_workout_metrics_pb2'
+ # @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsRequest)
+ })
+_sym_db.RegisterMessage(WorkoutMetricsRequest)
+
+WorkoutMetricsResponse = _reflection.GeneratedProtocolMessageType('WorkoutMetricsResponse', (_message.Message,), {
+ 'DESCRIPTOR' : _WORKOUTMETRICSRESPONSE,
+ '__module__' : 'rowing_workout_metrics_pb2'
+ # @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsResponse)
+ })
+_sym_db.RegisterMessage(WorkoutMetricsResponse)
+
+
+
+_METRICS = _descriptor.ServiceDescriptor(
+ name='Metrics',
+ full_name='rowing_workout_metrics.Metrics',
+ file=DESCRIPTOR,
+ index=0,
+ serialized_options=None,
+ serialized_start=284,
+ serialized_end=403,
+ methods=[
+ _descriptor.MethodDescriptor(
+ name='CalcMetrics',
+ full_name='rowing_workout_metrics.Metrics.CalcMetrics',
+ index=0,
+ containing_service=None,
+ input_type=_WORKOUTMETRICSREQUEST,
+ output_type=_WORKOUTMETRICSRESPONSE,
+ serialized_options=None,
+ ),
+])
+_sym_db.RegisterServiceDescriptor(_METRICS)
+
+DESCRIPTOR.services_by_name['Metrics'] = _METRICS
+
+# @@protoc_insertion_point(module_scope)
diff --git a/rowers/rowing_workout_metrics_pb2_grpc.py b/rowers/rowing_workout_metrics_pb2_grpc.py
new file mode 100644
index 00000000..b58e9986
--- /dev/null
+++ b/rowers/rowing_workout_metrics_pb2_grpc.py
@@ -0,0 +1,46 @@
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
+
+import rowers.rowing_workout_metrics_pb2 as rowing__workout__metrics__pb2
+
+
+class MetricsStub(object):
+ """OTW-metrics service definition
+ """
+
+ def __init__(self, channel):
+ """Constructor.
+
+ Args:
+ channel: A grpc.Channel.
+ """
+ self.CalcMetrics = channel.unary_unary(
+ '/rowing_workout_metrics.Metrics/CalcMetrics',
+ request_serializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.SerializeToString,
+ response_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.FromString,
+ )
+
+
+class MetricsServicer(object):
+ """OTW-metrics service definition
+ """
+
+ def CalcMetrics(self, request, context):
+ # missing associated documentation comment in .proto file
+ pass
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
+
+def add_MetricsServicer_to_server(servicer, server):
+ rpc_method_handlers = {
+ 'CalcMetrics': grpc.unary_unary_rpc_method_handler(
+ servicer.CalcMetrics,
+ request_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.FromString,
+ response_serializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.SerializeToString,
+ ),
+ }
+ generic_handler = grpc.method_handlers_generic_handler(
+ 'rowing_workout_metrics.Metrics', rpc_method_handlers)
+ server.add_generic_rpc_handlers((generic_handler,))
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..30e8ab61 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,ispromember
+
import stravalib
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
@@ -123,7 +125,7 @@ def get_strava_workout_list(user,limit_n=0):
# gets all new Strava workouts for a rower
def get_strava_workouts(rower):
- if not isprorower(rower):
+ if not ispromember(rower.user):
return 0
try:
@@ -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/tasks.py b/rowers/tasks.py
index 0a097d19..a0d9986e 100644
--- a/rowers/tasks.py
+++ b/rowers/tasks.py
@@ -38,6 +38,8 @@ from matplotlib import path
import grpc
import rowers.otw_power_calculator_pb2 as calculator_pb2
import rowers.otw_power_calculator_pb2_grpc as calculator_pb2_grpc
+import rowers.rowing_workout_metrics_pb2 as metrics_pb2
+import rowers.rowing_workout_metrics_pb2_grpc as metrics_pb2_grpc
from rowsandall_app.settings import SITE_URL
from rowsandall_app.settings_dev import SITE_URL as SITE_URL_DEV
@@ -609,105 +611,52 @@ def handle_calctrimp(id,
else:
engine = create_engine(database_url, echo=False)
- try:
- rowdata = rdata(csvfile=csvfilename)
- except IOError:
+ tss = 0
+ normp = 0
+ trimp = 0
+ hrtss = 0
+ normv = 0
+ normw = 0
+
+ # check what the real file name is
+ if os.path.exists(csvfilename):
+ csvfile = csvfilename
+ elif os.path.exists(csvfilename+'.csv'):
+ csvfile = csvfilename+'.csv'
+ elif os.path.exists(csvfilename+'.gz'):
+ csvfile = csvfilename+'.gz'
+
+ csvfile = os.path.abspath(csvfile)
+
+ with grpc.insecure_channel(
+ target='localhost:50052',
+ options=[('grpc.lb_policy_name', 'pick_first'),
+ ('grpc.enable_retries', 0), ('grpc.keepalive_timeout_ms',
+ 10000)]
+ ) as channel:
try:
- rowdata = rdata(csvfile=csvfilename + '.csv')
- except IOError:
- try:
- rowdata = rdata(csvfile=csvfilename + '.gz')
- except IOError:
- return 0
+ grpc.channel_ready_future(channel).result(timeout=10)
+ except grpc.FutureTimeoutError:
+ return 0
- df = rowdata.df
-
- try:
- df['deltat'] = df[' ElapsedTime (sec)'].diff().abs()
- except KeyError:
- return 0
-
- df2 = df.copy()
-
- df2['time'] = df2[' ElapsedTime (sec)']
- df2['time'] = df2['time'].apply(
- lambda x:safetimedelta(x)
+ stub = metrics_pb2_grpc.MetricsStub(channel)
+ req = metrics_pb2.WorkoutMetricsRequest(
+ filename = csvfile,
+ ftp=ftp,
+ sex=sex,
+ hrftp=hrftp,
+ hrmax=hrmax,
+ hrmin=hrmin,
)
+ response = stub.CalcMetrics(req,timeout=60)
- duration = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min()
- df2 = df2.resample('1s',on='time').mean()
- df2.fillna(method='ffill',inplace=True)
- df2 = df2.rolling(30).mean()
+ tss = response.tss
+ normp = response.normp
+ trimp = response.trimp
+ normv = response.normv
+ normw = response.normw
+ hrtss = response.hrtss
- df2[' Power (watts)'] = df2[' Power (watts)'].abs()
-
- df2['pwr4'] = df2[' Power (watts)']**(4.0)
- # pwr4mean = wavg(df,'pwr4','deltat')
- pwr4mean = df2['pwr4'].mean()
- pwrmean = df2[' Power (watts)'].mean()
- # pwrmean = wavg(df,' Power (watts)','deltat')
- if pwr4mean > 0:
- normp = (pwr4mean)**(0.25)
- else:
- normp = pwrmean
-
- intensityfactor = normp/float(ftp)
- tss = 100.*((duration*normp*intensityfactor)/(3600.*ftp))
-
-
- if sex == 'male':
- f = 1.92
- else:
- f = 1.67
-
- dt = df['TimeStamp (sec)'].diff()/60.
-
- hrr = (df[' HRCur (bpm)']-hrmin)/(hrmax-hrmin)
- hrrftp = (hrftp-hrmin)/float(hrmax-hrmin)
- trimp1hr = 60.*hrrftp*0.64*np.exp(f*hrrftp)
-
- trimpdata = dt*hrr*0.64*np.exp(f*hrr)
- trimp = trimpdata.sum()
-
- hrtss = 100.*trimp/trimp1hr
-
- pp = 8.0
-
- df['v4'] = df[' AverageBoatSpeed (m/s)']**(pp)
- v4mean = wavg(df,'v4','deltat')
- normv = v4mean**(1./pp)
-
- try:
- df['w4'] = df['driveenergy']**(pp)
- w4mean = wavg(df,'w4','deltat')
- normw = w4mean**(1./pp)
- except KeyError:
- normw = 0
-
-
- if not np.isfinite(normv):
- normv = 500./120.
-
- if not np.isfinite(normw):
- normw = 0
-
- try:
- dum = int(tss)
- except ValueError:
- tss = 0
-
- try:
- dum = int(normp)
- except ValueError:
- normp = 0
- try:
- dump = int(trimp)
- except ValueError:
- trimp = 0
- try:
- dump = int(hrtss)
- except ValueError:
- hrtss = 0
query = 'UPDATE rowers_workout SET rscore = {tss}, normp = {normp}, trimp={trimp}, hrtss={hrtss}, normv={normv}, normw={normw} WHERE id={id}'.format(
tss = int(tss),
diff --git a/rowers/teams.py b/rowers/teams.py
index 4c8ee0a4..aa1c8903 100644
--- a/rowers/teams.py
+++ b/rowers/teams.py
@@ -26,6 +26,8 @@ from rowers.models import (
CoachingGroup
)
+from rowers.rower_rules import is_team_manager,is_team_member,is_coach
+
from rowers.tasks import (
handle_sendemail_invite,
handle_sendemail_request,
@@ -53,14 +55,15 @@ def handle_remove_workouts_team(ws,t):
return 1
def handle_add_workouts_team(ws,t):
-
+
for w in ws:
w.team.add(t)
return 1
def update_team(t,name,manager,private,notes,viewing):
- if t.manager != manager:
+
+ if not is_team_manager(manager,t):
return (0,'You are not the manager of this team')
try:
t.name = name
@@ -78,11 +81,11 @@ def create_team(name,manager,private='open',notes='',viewing='allmembers'):
if manager.rower.rowerplan == 'basic':
if manager.rower.protrialexpires < timezone.now().date() and manager.rower.plantrialexpires < timezone.now().date():
return (0,'You need to upgrade to a paid plan to establish a team')
- if manager.rower.rowerplan not in ('coach','freecoach'):
+ if not is_coach(manager):
ts = Team.objects.filter(manager=manager)
if len(ts)>=1:
return (0,'You need to upgrade to the Coach plan to have more than one team')
-
+
try:
t = Team(name=name,manager=manager,notes=notes,
private=private,viewing=viewing)
@@ -99,24 +102,11 @@ def remove_team(id):
send_team_delete_mail(t,r)
return t.delete()
-#def set_teamplanexpires(rower):
-# ts = Team.objects.filter(rower=rower)
-
-# texp = datetime.date(timezone.now())
-
-# for t in ts:
-# print t.name
-# mr = Rower.objects.get(user=t.manager)
-# if mr.teamplanexpires > texp:
-# rower.teamplanexpires = mr.teamplanexpires
-
-# t.save()
-
return (1,'Updated rower team expiry')
def add_coach(coach,rower):
# get coaching group
-
+
coachgroup = coach.mycoachgroup
if coachgroup is None:
coachgroup = CoachingGroup(name=coach.user.first_name)
@@ -126,7 +116,7 @@ def add_coach(coach,rower):
if get_coach_club_size(coach) invitation.issuedate+timedelta(days=inviteduration):
revoke_invite(invitation.team.manager,invitation.id)
return (0,'The invitation has expired')
-
+
t = invitation.team
result, comment = add_member(t.id,r)
if not result:
@@ -698,7 +683,7 @@ def process_coachoffer_code(user,code):
return result
else:
send_coachoffer_accepted_email(rekwest)
-
+
rekwest.delete()
return result
@@ -732,7 +717,7 @@ def send_coachrequest_accepted_email(rekwest):
res = myqueue(queuehigh,
handle_sendemail_coachrequest_accepted,
email,coachname,name)
-
+
def send_coachoffer_accepted_email(rekwest):
coachname = rekwest.coach.user.first_name + " " + rekwest.coach.user.last_name
@@ -743,5 +728,3 @@ def send_coachoffer_accepted_email(rekwest):
res = myqueue(queuehigh,
handle_sendemail_coachoffer_accepted,
coachemail,coachname,name)
-
-
diff --git a/rowers/templates/menu_other.html b/rowers/templates/menu_other.html
index fad56ed0..a3961006 100644
--- a/rowers/templates/menu_other.html
+++ b/rowers/templates/menu_other.html
@@ -14,7 +14,7 @@
{% else %}
Compare
{% endif %}
-
+
Upload
@@ -33,7 +33,7 @@
-
+
-
-
{% include 'menuscript.html' %}
diff --git a/rowers/templates/menu_profile.html b/rowers/templates/menu_profile.html
index bdeb6f54..05cf98f1 100644
--- a/rowers/templates/menu_profile.html
+++ b/rowers/templates/menu_profile.html
@@ -44,13 +44,13 @@
{% if user.is_authenticated and user|is_manager and rower %}
-{% if user|coach_rowers %}
+{% if request.user|coach_rowers %}
""" % (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/analysisviews.py b/rowers/views/analysisviews.py
index 4cf62a9e..d493f6b5 100644
--- a/rowers/views/analysisviews.py
+++ b/rowers/views/analysisviews.py
@@ -33,6 +33,7 @@ defaultoptions = {
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def analysis_new(request,userid=0,function='boxplot',teamid=0):
r = getrequestrower(request, userid=userid)
user = r.user
@@ -669,6 +670,7 @@ def boxplotdata(workouts,options):
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def analysis_view_data(request,userid=0):
if not request.is_ajax():
url = reverse('analysis_new')
@@ -727,7 +729,8 @@ def analysis_view_data(request,userid=0):
@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 histo(request,theuser=0,
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
+def histo(request,userid=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
deltadays=-1,
@@ -741,7 +744,7 @@ def histo(request,theuser=0,
'histoparam':'power'
}):
- r = getrequestrower(request,userid=theuser)
+ r = getrequestrower(request,userid=userid)
theuser = r.user
if 'histoparam' in request.session:
@@ -1327,17 +1330,18 @@ def cum_flex(request,theuser=0,
def planrequired_view(request):
messages.info(request,"This functionality requires Coach or Self-Coach membership")
- return HttpResponseRedirect(reverse('paidplans'))
+ return HttpResponseRedirect(reverse('paidplans_view'))
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
+@user_passes_test(isplanmember,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
-def fitnessmetric_view(request,id=0,mode='rower',
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
+def fitnessmetric_view(request,userid=0,mode='rower',
startdate=timezone.now()-timezone.timedelta(days=365),
enddate=timezone.now()):
- therower = getrequestrower(request,userid=id)
+ therower = getrequestrower(request,userid=userid)
theuser = therower.user
@@ -1387,7 +1391,8 @@ def fitnessmetric_view(request,id=0,mode='rower',
# Show ranking distances including predicted paces
@login_required()
-def rankings_view(request,theuser=0,
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
+def rankings_view(request,userid=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
deltadays=-1,
@@ -1408,25 +1413,25 @@ def rankings_view(request,theuser=0,
enddate = startdate
startdate = s
- if theuser == 0:
- theuser = request.user.id
+
promember=0
- if not request.user.is_anonymous:
- r = getrower(request.user)
- if r.birthdate:
- age = calculate_age(r.birthdate)
- worldclasspower = int(metrics.getagegrouprecord(
- age,
- sex=r.sex,
- weightcategory=r.weightcategory,
- ))
- else:
- worldclasspower = None
+ r = getrequestrower(request,userid=userid)
+ theuser = r.user.id
- result = request.user.is_authenticated and ispromember(request.user)
- if result:
- promember=1
+ if r.birthdate:
+ age = calculate_age(r.birthdate)
+ worldclasspower = int(c2stuff.getagegrouprecord(
+ age,
+ sex=r.sex,
+ weightcategory=r.weightcategory,
+ ))
+ else:
+ worldclasspower = None
+
+ result = request.user.is_authenticated and ispromember(request.user)
+ if result:
+ promember=1
# get all indoor rows in date range
@@ -1737,7 +1742,8 @@ def ajax_agegrouprecords(request,
# Show ranking distances including predicted paces
@login_required()
-def rankings_view2(request,theuser=0,
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
+def rankings_view2(request,userid=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
deltadays=-1,
@@ -1758,83 +1764,84 @@ def rankings_view2(request,theuser=0,
enddate = startdate
startdate = s
- if theuser == 0:
- theuser = request.user.id
+ if userid == 0:
+ userid = request.user.id
else:
lastupdated = "01-01-1900"
promember=0
- if not request.user.is_anonymous:
- r = getrower(request.user)
+ r = getrequestrower(request,userid=userid)
+ theuser = r.user
+
+ wcdurations = []
+ wcpower = []
+
+ lastupdated = "01-01-1900"
+ userid = 0
+ if 'options' in request.session:
+ options = request.session['options']
+ try:
+ wcdurations = options['wcdurations']
+ wcpower = options['wcpower']
+ lastupdated = options['lastupdated']
+ except KeyError:
+ pass
+ try:
+ userid = options['userid']
+ except KeyError:
+ userid = 0
+ else:
+ options = {}
+
+
+
+ lastupdatedtime = arrow.get(lastupdated).timestamp
+ current_time = arrow.utcnow().timestamp
+
+ deltatime_seconds = current_time - lastupdatedtime
+ recalc = False
+ if str(userid) != str(theuser) or deltatime_seconds > 3600:
+ recalc = True
+ options['lastupdated'] = arrow.utcnow().isoformat()
+ else:
+ recalc = False
+
+ options['userid'] = theuser.id
+
+ if r.birthdate:
+ age = calculate_age(r.birthdate)
+ else:
+ worldclasspower = None
+ age = 0
+
+ agerecords = CalcAgePerformance.objects.filter(
+ age = age,
+ sex = r.sex,
+ weightcategory = r.weightcategory)
+
+ if len(agerecords) == 0:
+ recalc = True
+ wcpower = []
+ wcduration = []
+ else:
wcdurations = []
wcpower = []
+ for record in agerecords:
+ wcdurations.append(record.duration)
+ wcpower.append(record.power)
- lastupdated = "01-01-1900"
- userid = 0
- if 'options' in request.session:
- options = request.session['options']
- try:
- wcdurations = options['wcdurations']
- wcpower = options['wcpower']
- lastupdated = options['lastupdated']
- except KeyError:
- pass
- try:
- userid = options['userid']
- except KeyError:
- userid = 0
- else:
- options = {}
+ options['wcpower'] = wcpower
+ options['wcdurations'] = wcdurations
+ if theuser:
+ options['userid'] = theuser.id
+
+ request.session['options'] = options
-
- lastupdatedtime = arrow.get(lastupdated).timestamp
- current_time = arrow.utcnow().timestamp
-
- deltatime_seconds = current_time - lastupdatedtime
- recalc = False
- if str(userid) != str(theuser) or deltatime_seconds > 3600:
- recalc = True
- options['lastupdated'] = arrow.utcnow().isoformat()
- else:
- recalc = False
-
- options['userid'] = theuser
-
- if r.birthdate:
- age = calculate_age(r.birthdate)
- else:
- worldclasspower = None
- age = 0
-
- agerecords = CalcAgePerformance.objects.filter(
- age = age,
- sex = r.sex,
- weightcategory = r.weightcategory)
-
- if len(agerecords) == 0:
- recalc = True
- wcpower = []
- wcduration = []
- else:
- wcdurations = []
- wcpower = []
- for record in agerecords:
- wcdurations.append(record.duration)
- wcpower.append(record.power)
-
- options['wcpower'] = wcpower
- options['wcdurations'] = wcdurations
- if theuser:
- options['userid'] = theuser
-
- request.session['options'] = options
-
-
- result = request.user.is_authenticated and ispromember(request.user)
- if result:
- promember=1
+ result = request.user.is_authenticated and ispromember(request.user)
+ if result:
+ promember=1
# get all indoor rows in date range
@@ -1883,10 +1890,7 @@ def rankings_view2(request,theuser=0,
r=0
- try:
- uu = User.objects.get(id=theuser)
- except User.DoesNotExist:
- uu = ''
+ uu = theuser
# test to fix bug
@@ -2150,7 +2154,8 @@ def rankings_view2(request,theuser=0,
@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 otwrankings_view(request,theuser=0,
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
+def otwrankings_view(request,userid=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
startdatestring="",
@@ -2173,22 +2178,10 @@ def otwrankings_view(request,theuser=0,
enddate = startdate
startdate = s
- if theuser == 0:
- if 'rowerid' in request.session:
- try:
- r = Rower.objects.get(id=request.session['rowerid'])
- theuser = r.user.id
- except Rower.DoesNotExist:
- theuser = request.user.id
- else:
- theuser = request.user.id
+ r = getrequestrower(request,userid=userid)
+ promember = True
- promember=0
- if not request.user.is_anonymous:
- r = Rower.objects.get(user=request.user)
- result = request.user.is_authenticated and ispromember(request.user)
- if result:
- promember=1
+ theuser = r.user
# get all OTW rows in date range
@@ -2242,10 +2235,7 @@ def otwrankings_view(request,theuser=0,
- try:
- uu = User.objects.get(id=theuser)
- except User.DoesNotExist:
- uu = ''
+ uu = theuser
# test to fix bug
@@ -2555,7 +2545,8 @@ def otwcp_toadmin_view(request,theuser=0,
@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 oterankings_view(request,theuser=0,
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
+def oterankings_view(request,userid=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
startdatestring="",
@@ -2578,23 +2569,11 @@ def oterankings_view(request,theuser=0,
enddate = startdate
startdate = s
- if theuser == 0:
- if 'rowerid' in request.session:
- try:
- r = Rower.objects.get(id=request.session['rowerid'])
- theuser = r.user.id
- except Rower.DoesNotExist:
- theuser = request.user.id
- else:
- theuser = request.user.id
+ r = getrequestrower(request,userid=userid)
+ theuser = r.user
promember=0
- if not request.user.is_anonymous:
- r = Rower.objects.get(user=request.user)
- result = request.user.is_authenticated and ispromember(request.user)
- if result:
- promember=1
# get all OTW rows in date range
@@ -2668,10 +2647,8 @@ def oterankings_view(request,theuser=0,
raise Http404("Rower doesn't exist")
- try:
- uu = User.objects.get(id=theuser)
- except User.DoesNotExist:
- uu = ''
+ uu = theuser
+
# test to fix bug
@@ -2906,6 +2883,7 @@ def oterankings_view(request,theuser=0,
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def user_multiflex_select(request,
startdatestring="",
enddatestring="",
@@ -3112,6 +3090,7 @@ def user_multiflex_select(request,
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def multiflex_data(request,userid=0,
options={
'includereststrokes':False,
@@ -3365,6 +3344,7 @@ def multiflex_data(request,userid=0,
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def multiflex_view(request,userid=0,
options={
'includereststrokes':False,
@@ -3532,6 +3512,7 @@ def multiflex_view(request,userid=0,
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def user_boxplot_select(request,
startdatestring="",
enddatestring="",
@@ -3740,6 +3721,7 @@ def user_boxplot_select(request,
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def boxplot_view_data(request,userid=0,
options={
'includereststrokes':False,
@@ -3857,6 +3839,7 @@ def boxplot_view_data(request,userid=0,
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def boxplot_view(request,userid=0,
options={
'includereststrokes':False,
@@ -3975,7 +3958,8 @@ def boxplot_view(request,userid=0,
# Cumulative stats page
@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 cumstats(request,theuser=0,
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
+def cumstats(request,userid=0,
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now(),
deltadays=-1,
@@ -3988,7 +3972,7 @@ def cumstats(request,theuser=0,
'rankingonly':False,
}):
- r = getrequestrower(request,userid=theuser)
+ r = getrequestrower(request,userid=userid)
theuser = r.user
if 'waterboattype' in request.session:
@@ -4106,7 +4090,7 @@ def cumstats(request,theuser=0,
options = {
'modality': modality,
- 'theuser': theuser.id,
+ 'userid': theuser.id,
'waterboattype':waterboattype,
'startdatestring':startdatestring,
'enddatestring':enddatestring,
@@ -4326,6 +4310,7 @@ def agegrouprecordview(request,sex='male',weightcategory='hwt',
# 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)
@login_required
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def alerts_view(request,userid=0):
r = getrequestrower(request,userid=userid)
@@ -4360,6 +4345,7 @@ def alerts_view(request,userid=0):
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def alert_create_view(request,userid=0):
r = getrequestrower(request,userid=userid)
FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=1)
@@ -4438,6 +4424,7 @@ def alert_create_view(request,userid=0):
# alert report view
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def alert_report_view(request,id=0,userid=0,nperiod=0):
r = getrequestrower(request,userid=userid)
if userid == 0:
@@ -4495,6 +4482,7 @@ def alert_report_view(request,id=0,userid=0,nperiod=0):
@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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def alert_edit_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
diff --git a/rowers/views/exportviews.py b/rowers/views/exportviews.py
index 4b5f4684..1b6e0e85 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=get_workout_by_opaqueid,raise_exception=True)
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=get_workout_by_opaqueid,raise_exception=True)
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=get_workout_by_opaqueid,raise_exception=True)
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())
@@ -252,18 +249,22 @@ def workout_csvemail_view(request,id=0):
rowdate = rowdata.rowdatetime
starttimeunix = arrow.get(rowdate).timestamp
df = rowdata.df
- df[' ElapsedTime (sec)'] = df['TimeStamp (sec)']
- df['TimeStamp (sec)'] = df['TimeStamp (sec)'] + starttimeunix
-
+ try:
+ df[' ElapsedTime (sec)'] = df['TimeStamp (sec)']
+ df['TimeStamp (sec)'] = df['TimeStamp (sec)'] + starttimeunix
+ except KeyError:
+ pass
+
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()
+@permission_required('rower.is_staff',fn=get_user_by_userid,raise_exception=True)
def workout_csvtoadmin_view(request,id=0):
message = ""
r = getrower(request.user)
diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py
index 2f97d00c..8e9ecec4 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=get_workout_by_opaqueid,raise_exception=True)
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_workout_by_opaqueid(request,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=get_workout_by_opaqueid,raise_exception=True)
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=get_workout_by_opaqueid,raise_exception=True)
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=get_workout_by_opaqueid,raise_exception=True)
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=get_workout_by_opaqueid)
def workout_sporttracks_upload_view(request,id=0):
message = ""
# ready to upload. Hurray
@@ -347,51 +337,48 @@ 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'}
-
- url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
- 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.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:
- s = response
- message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason
- messages.error(request,message)
- else:
- message = "You are not authorized to upload this workout"
+ 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,
+ 'user-agent': 'sanderroosendaal',
+ 'Content-Type': 'application/json'}
+
+ url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
+ 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.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:
+ s = response
+ message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason
+ messages.error(request,message)
+
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
@@ -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,27 +879,28 @@ def rower_process_testcallback(request):
text += "\n\nRefresh Token:\n"
text += refresh_token
-
+
return HttpResponse(text)
# The page where you select which Strava workout to import
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def workout_stravaimport_view(request,message="",userid=0):
r = getrequestrower(request,userid=userid)
- if r.user != request.user:
- messages.info(request,"You cannot import other people's workouts from Strava")
+ #if r.user != request.user:
+ # messages.info(request,"You cannot import other people's workouts from Strava")
try:
thetoken = strava_open(request.user)
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 +938,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 +973,7 @@ def workout_stravaimport_view(request,message="",userid=0):
r = getrower(request.user)
-
+
return render(request,'strava_list_import.html',
{'workouts':workouts,
'rower':r,
@@ -998,6 +986,7 @@ def workout_stravaimport_view(request,message="",userid=0):
# The page where you select which RunKeeper workout to import
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def workout_runkeeperimport_view(request,message="",userid=0):
res = runkeeperstuff.get_runkeeper_workout_list(request.user)
if (res.status_code != 200):
@@ -1024,7 +1013,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)
@@ -1053,6 +1042,7 @@ def workout_runkeeperimport_view(request,message="",userid=0):
# The page where you select which RunKeeper workout to import
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def workout_underarmourimport_view(request,message="",userid=0):
res = underarmourstuff.get_underarmour_workout_list(request.user)
if (res.status_code != 200):
@@ -1072,11 +1062,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 +1080,7 @@ def workout_underarmourimport_view(request,message="",userid=0):
'name':'Concept2'
},
]
-
+
return render(request,'underarmour_list_import.html',
{'workouts':workouts,
'breadcrumbs':breadcrumbs,
@@ -1103,6 +1093,7 @@ def workout_underarmourimport_view(request,message="",userid=0):
# the page where you select which Polar workout to Import
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def workout_polarimport_view(request,userid=0):
exercises = polarstuff.get_polar_workouts(request.user)
workouts = []
@@ -1115,13 +1106,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 +1147,14 @@ def workout_polarimport_view(request,userid=0):
})
-
+
# The page where you select which SportTracks workout to import
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
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 +1207,7 @@ def workout_sporttracksimport_view(request,message="",userid=0):
'name':'SportTracks'
},
]
-
+
return render(request,'sporttracks_list_import.html',
{'workouts':workouts,
'breadcrumbs':breadcrumbs,
@@ -1225,7 +1217,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 +1254,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,31 +1280,32 @@ 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()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def workout_c2import_view(request,page=1,userid=0,message=""):
r = getrequestrower(request,userid=userid)
- if r.user != request.user:
- messages.info(request,"You cannot import other people's workouts from Concept2")
+ # if r.user != request.user:
+ # messages.info(request,"You cannot import other people's workouts from Concept2")
+
+ # r = getrower(request.user)
- r = getrower(request.user)
-
try:
thetoken = c2_open(request.user)
except NoTokenError:
@@ -1366,7 +1359,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 +1387,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 +1426,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 +1456,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 +1497,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 +1527,7 @@ def workout_getimportview(request,externalid,source = 'c2'):
})
return HttpResponseRedirect(url)
-
+
@@ -1599,7 +1592,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 +1601,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/otherviews.py b/rowers/views/otherviews.py
index 1e9572ba..aa28d89b 100644
--- a/rowers/views/otherviews.py
+++ b/rowers/views/otherviews.py
@@ -49,8 +49,8 @@ def failed_queue_empty(request):
q.empty()
return HttpResponseRedirect(reverse('failed_queue_view'))
-
-
+
+
@login_required()
def failed_job_view(request,id=0):
if not request.user.is_staff:
@@ -62,7 +62,7 @@ def failed_job_view(request,id=0):
thejob.delete()
return HttpResponseRedirect(reverse('failed_queue_view'))
-
+
@login_required()
def errormessage_view(request,errormessage='aap'):
@@ -74,123 +74,10 @@ def errormessage_view(request,errormessage='aap'):
"result":1,
})
-@login_required()
-def deactivate_user(request):
- pk = request.user.id
- user = User.objects.get(pk=pk)
- user_form = DeactivateUserForm(instance=user)
- if request.user.is_authenticated and request.user.id == user.id:
- if request.method == "POST":
- user_form = DeactivateUserForm(request.POST, instance=user)
- if user_form.is_valid():
- if not user_form.cleaned_data['is_active']:
- r = Rower.objects.get(user=user)
- if r.paidplan is not None and r.paidplan.paymentprocessor == 'braintree':
- try:
- subscriptions = braintreestuff.find_subscriptions(r)
- for subscription in subscriptions:
- success, themessages,errormessages = braintreestuff.cancel_subscription(r,id)
- for message in themessages:
- messages.info(request,message)
- except ProcessorCustomerError:
- pass
-
- r.paidplan = None
- r.teamplanexpires = timezone.now()
- r.planexpires = timezone.now()
- r.clubsize = 0
- r.rowerplan = 'basic'
- r.save()
-
- deactivate_user = user_form.save(commit=False)
- user.is_active = False
- user.save()
- deactivate_user.save()
- # url = reverse(auth_views.logout_then_login)
- url = '/logout/?next=/login'
- return HttpResponseRedirect(url)
-
- return render(request, "userprofile_deactivate.html", {
- "user_form": user_form,
- })
- else:
- raise PermissionDenied
-
-@login_required()
-def user_gdpr_optin(request):
- r = getrower(request.user)
- r.gdproptin = False
- r.gdproptindate = None
- r.save()
- nexturl = request.GET.get('next','/rowers/list-workouts/')
- if r.gdproptin:
- return HttpResponseRedirect(nexturl)
-
- return render(request,'gdpr_optin.html',{
- "next": nexturl
- })
-
-@login_required()
-def user_gdpr_confirm(request):
- r = getrower(request.user)
- r.gdproptin = True
- r.gdproptindate = timezone.now()
- r.save()
-
- nexturl = request.GET.get('next','/rowers/list-workouts/')
-
- return HttpResponseRedirect(nexturl)
-
-
-
-@login_required()
-def remove_user(request):
- pk = request.user.id
- user = User.objects.get(pk=pk)
- user_form = DeleteUserForm(instance=user)
- if request.user.is_authenticated and request.user.id == user.id:
- if request.method == "POST":
- user_form = DeleteUserForm(request.POST,instance=user)
- if user_form.is_valid():
- cd = user_form.cleaned_data
- name = user.first_name+' '+user.last_name
- email = user.email
-
- r = Rower.objects.get(user=user)
- if r.paidplan is not None and r.paidplan.paymentprocessor == 'braintree':
- try:
- subscriptions = braintreestuff.find_subscriptions(r)
- for subscription in subscriptions:
- success, themessages,errormessages = braintreestuff.cancel_subscription(r,id)
- for message in themessages:
- messages.info(request,message)
- except:
- pass
-
- if cd['delete_user']:
- # delete workouts
- Workout.objects.filter(user=user.rower).delete()
- # delete planned sessions
- PlannedSession.objects.filter(manager=user).delete()
-
- user.delete()
- res = myqueue(queuehigh,
- handle_sendemail_userdeleted,
- name, email)
-
- url = '/logout/?next=/login'
-# url = reverse(auth_views.logout_then_login)
- return HttpResponseRedirect(url)
- return render(request, "userprofile_delete.html", {
- "user_form": user_form,
- })
- else:
- raise PermissionDenied
-
-
# Shows analysis page
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def analysis_view(request,userid=0):
r = getrequestrower(request,userid=userid)
return render(request,
@@ -200,9 +87,10 @@ def analysis_view(request,userid=0):
'rower':r,
}
)
-
+
# Shows laboratory page
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def laboratory_view(request,userid=0):
r = getrequestrower(request,userid=userid)
return render(request,
@@ -212,9 +100,3 @@ def laboratory_view(request,userid=0):
'rower':r,
}
)
-
-
-
-
-
-
diff --git a/rowers/views/paymentviews.py b/rowers/views/paymentviews.py
index 34a1f133..344f84ac 100644
--- a/rowers/views/paymentviews.py
+++ b/rowers/views/paymentviews.py
@@ -13,8 +13,8 @@ def paidplans_view(request):
else:
r = None
-
-
+
+
return render(request,
'paidplans.html',
{'rower':r})
@@ -24,7 +24,7 @@ def billing_view(request):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
r = request.user.rower
if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring':
@@ -42,7 +42,7 @@ def billing_view(request):
for attr, value in cd.items():
setattr(r, attr, value)
r.save()
-
+
if billingaddressform.is_valid():
if planselectform.is_valid():
plan = planselectform.cleaned_data['plan']
@@ -58,7 +58,7 @@ def billing_view(request):
})
return HttpResponseRedirect(url)
-
+
else:
billingaddressform = RowerBillingAddressForm(instance=r)
planselectform = PlanSelectForm(paymentprocessor='braintree')
@@ -69,13 +69,13 @@ def billing_view(request):
'billingaddressform':billingaddressform,
'planselectform':planselectform,
})
-
+
@login_required()
def upgrade_view(request):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
r = request.user.rower
if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring':
@@ -93,7 +93,7 @@ def upgrade_view(request):
for attr, value in cd.items():
setattr(r, attr, value)
r.save()
-
+
if planselectform.is_valid():
plan = planselectform.cleaned_data['plan']
if billingaddressform.is_valid():
@@ -102,7 +102,7 @@ def upgrade_view(request):
'planid':plan.id
})
return HttpResponseRedirect(url)
-
+
else:
billingaddressform = RowerBillingAddressForm(instance=r)
planselectform = PlanSelectForm(paymentprocessor='braintree',
@@ -114,13 +114,13 @@ def upgrade_view(request):
'billingaddressform':billingaddressform,
'planselectform':planselectform,
})
-
+
@login_required()
def downgrade_view(request):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
r = request.user.rower
if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring':
@@ -138,7 +138,7 @@ def downgrade_view(request):
for attr, value in cd.items():
setattr(r, attr, value)
r.save()
-
+
if planselectform.is_valid():
plan = planselectform.cleaned_data['plan']
@@ -150,14 +150,14 @@ def downgrade_view(request):
return HttpResponseRedirect(url)
else:
nextview = downgrade_confirm_view
-
+
if billingaddressform.is_valid():
url = reverse(nextview,
kwargs={
'planid':plan.id
})
return HttpResponseRedirect(url)
-
+
else:
billingaddressform = RowerBillingAddressForm(instance=r)
planselectform = PlanSelectForm(paymentprocessor='braintree',
@@ -169,17 +169,17 @@ def downgrade_view(request):
'billingaddressform':billingaddressform,
'planselectform':planselectform,
})
-
+
@login_required()
def plan_stop_view(request):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
r = request.user.rower
subscriptions = []
-
+
if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring':
messages.error(request,'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed')
@@ -189,7 +189,7 @@ def plan_stop_view(request):
except ProcessorCustomerError:
r.paymentprocessor = None
r.save()
-
+
return render(request,
@@ -203,7 +203,7 @@ def plan_tobasic_view(request,id=0):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
r = request.user.rower
if r.paidplan.paymentprocessor == 'braintree':
@@ -217,15 +217,15 @@ def plan_tobasic_view(request,id=0):
url = reverse(plan_stop_view)
return HttpResponseRedirect(url)
-
-
+
+
@login_required()
def upgrade_confirm_view(request,planid = 0):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
try:
plan = PaidPlan.objects.get(id=planid)
except PaidPlan.DoesNotExist:
@@ -253,7 +253,7 @@ def downgrade_confirm_view(request,planid = 0):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
try:
plan = PaidPlan.objects.get(id=planid)
except PaidPlan.DoesNotExist:
@@ -279,7 +279,7 @@ def payment_confirm_view(request,planid = 0):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
try:
plan = PaidPlan.objects.get(id=planid)
except PaidPlan.DoesNotExist:
@@ -350,13 +350,13 @@ def checkouts_view(request):
url = reverse(paidplans_view)
return HttpResponseRedirect(url)
-
+
@login_required()
def upgrade_checkouts_view(request):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
r = request.user.rower
@@ -402,7 +402,7 @@ def downgrade_checkouts_view(request):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
r = request.user.rower
@@ -450,7 +450,7 @@ def payment_completed_view(request):
amount = request.GET.get('amount',0)
-
+
r = request.user.rower
return render(request,
@@ -465,7 +465,7 @@ def downgrade_completed_view(request):
if not PAYMENT_PROCESSING_ON:
url = reverse('promembership')
return HttpResponseRedirect(url)
-
+
r = request.user.rower
return render(request,
@@ -480,7 +480,7 @@ def rower_register_view(request):
nextpage = request.GET.get('next','/rowers/list-workouts/')
if nextpage == '':
nextpage = '/rowers/list-workouts/'
-
+
if request.method == 'POST':
#form = RegistrationFormUniqueEmail(request.POST)
form = RegistrationFormSex(request.POST)
@@ -517,7 +517,7 @@ def rower_register_view(request):
timestr = strftime("%Y%m%d-%H%M%S")
f2 = f[:-7]+timestr+'.csv.gz'
copyfile(f,f2)
-
+
response = dataprep.new_workout_from_file(therower,f2,
title='New User Sample Data',
notes='This is an example workout to get you started')
@@ -527,17 +527,17 @@ def rower_register_view(request):
w.startdatetime = timezone.now()
w.date = timezone.now().date()
w.save()
-
+
# Create and send email
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "Thank you for registering on rowsandall.com"
from_address = 'Sander Roosendaal '
d = {'first_name':theuser.first_name}
-
+
send_template_email(from_address,[fullemail],
subject,'registeremail.html',d)
-
+
subject2 = "New User"
message2 = "New user registered.\n"
@@ -550,7 +550,7 @@ def rower_register_view(request):
theuser = authenticate(username=username,password=password)
login(request,theuser)
-
+
return HttpResponseRedirect(nextpage)
# '/rowers/register/thankyou/')
@@ -572,7 +572,7 @@ def freecoach_register_view(request):
nextpage = request.GET.get('next','/rowers/me/teams/')
if nextpage == '':
nextpage = '/rowers/me/teams/'
-
+
if request.method == 'POST':
#form = RegistrationFormUniqueEmail(request.POST)
form = RegistrationFormSex(request.POST)
@@ -605,17 +605,17 @@ def freecoach_register_view(request):
# create default favorite charts
add_defaultfavorites(therower)
-
+
# Create and send email
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "Thank you for registering on rowsandall.com"
from_address = 'Sander Roosendaal '
d = {'first_name':theuser.first_name}
-
+
send_template_email(from_address,[fullemail],
subject,'coachregisteremail.html',d)
-
+
subject2 = "New Free Coach"
message2 = "New Free Coach registered.\n"
@@ -628,7 +628,7 @@ def freecoach_register_view(request):
theuser = authenticate(username=username,password=password)
login(request,theuser)
-
+
return HttpResponseRedirect(nextpage)
else:
@@ -647,6 +647,7 @@ def freecoach_register_view(request):
'next':nextpage,})
@login_required()
+@permission_required('rower.is_staff',fn=get_user_by_userid,raise_exception=True)
def transactions_view(request):
if not request.user.is_staff:
raise PermissionDenied("Not Allowed")
@@ -662,7 +663,7 @@ def transactions_view(request):
response = HttpResponse(df.to_csv())
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
response['Content-Type'] = 'application/octet-stream'
-
+
return response
else:
@@ -673,4 +674,3 @@ def transactions_view(request):
{
'dateform':dateform
})
-
diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py
index 7dabf62f..f4ff5fbc 100644
--- a/rowers/views/planviews.py
+++ b/rowers/views/planviews.py
@@ -5,27 +5,11 @@ from __future__ import unicode_literals
from rowers.views.statements import *
-@login_required()
+@login_required
+@permission_required('plannedsession.view_session',fn=get_session_by_pk,raise_exception=True)
def plannedsession_comment_view(request,id=0,userid=0):
r = getrequestplanrower(request,userid=userid)
-
- try:
- ps = PlannedSession.objects.get(id=id)
- except PlannedSession.DoesNotExist:
- raise Http404("Planned Session does not exist")
-
- m = ps.manager
- mm = Rower.objects.get(user=m)
-
- if ps.manager != request.user and ps.sessiontype not in ['race','indoorrace']:
- if r.rowerplan == 'coach' and r not in ps.rower.all():
- teams = Team.objects.filter(manager=request.user)
- members = Rower.objects.filter(team__in=teams).distinct()
- teamusers = [m.user for m in members]
- if ps.manager not in teamusers:
- raise PermissionDenied("You do not have access to this session")
- elif r not in ps.rower.all():
- raise PermissionDenied("You do not have access to this session")
+ ps = get_object_or_404(PlannedSession,pk=id)
comments = PlannedSessionComment.objects.filter(plannedsession=ps).order_by("created")
@@ -171,7 +155,7 @@ def plannedsession_comment_view(request,id=0,userid=0):
})
# Cloning sessions
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
+@user_passes_test(can_plan,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_multiclone_view(
@@ -322,7 +306,7 @@ def plannedsession_multiclone_view(
)
# Individual user creates training for himself
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
+@user_passes_test(can_plan,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_create_view(request,
@@ -332,9 +316,6 @@ def plannedsession_create_view(request,
r = getrequestplanrower(request,userid=userid)
-
-
-
startdate,enddate = get_dates_timeperiod(request,startdatestring=startdatestring,
enddatestring=enddatestring)
@@ -477,7 +458,7 @@ def plannedsession_create_view(request,
'timeperiod':timeperiod,
})
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
+@user_passes_test(can_plan,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_multicreate_view(request,
@@ -613,7 +594,7 @@ def plannedsession_multicreate_view(request,
return render(request,'plannedsession_multicreate.html',context)
# Manager creates sessions for entire team
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
+@user_passes_test(can_plan,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_teamcreate_view(request,
@@ -788,21 +769,16 @@ def plannedsession_teamcreate_view(request,
})
# Manager edits sessions for entire team
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
+@user_passes_test(can_plan,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
+@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True)
def plannedsession_teamedit_view(request,
- sessionid=0,userid=0):
+ id=0,userid=0):
r = getrequestplanrower(request,userid=userid)
-
- try:
- ps = PlannedSession.objects.get(id=sessionid)
- except PlannedSession.DoesNotExist:
- raise Http404("This session doesn't exist")
- if not ps.manager == request.user:
- raise PermissionDenied("You are not the manager of this session")
+ ps = get_object_or_404(PlannedSession,pk=id)
teams = Team.objects.filter(manager=request.user)
teamchoices = [(team.id, team.name) for team in teams]
@@ -892,7 +868,7 @@ def plannedsession_teamedit_view(request,
url = reverse(plannedsession_teamedit_view,
kwargs = {
- 'sessionid':sessionid,
+ 'id':id,
})
startdatestring = startdate.strftime('%Y-%m-%d')
@@ -1240,6 +1216,7 @@ def plannedsessions_print_view(request,userid=0):
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def plannedsessions_manage_view(request,userid=0,
initialsession=0):
@@ -1247,8 +1224,6 @@ def plannedsessions_manage_view(request,userid=0,
if request.is_ajax():
is_ajax = True
-
-
r = getrequestrower(request,userid=userid)
startdate,enddate = get_dates_timeperiod(request)
@@ -1404,7 +1379,8 @@ def plannedsessions_manage_view(request,userid=0,
# Clone an existing planned session
# need clarity on cloning behavior time shift
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
+@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True)
+@user_passes_test(can_plan,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_clone_view(request,id=0,userid=0):
@@ -1422,13 +1398,7 @@ def plannedsession_clone_view(request,id=0,userid=0):
except IndexError:
trainingplan = None
- try:
- ps = PlannedSession.objects.get(id=id)
- except PlannedSession.DoesNotExist:
- raise Http404("Planned Session does not exist")
-
- if ps.manager != request.user:
- raise PermissionDenied("You are not allowed to clone this planned session")
+ ps = get_object_or_404(PlannedSession,pk=id)
rowers = ps.rower.all()
teams = ps.team.all()
@@ -1472,7 +1442,8 @@ def plannedsession_clone_view(request,id=0,userid=0):
# Clone an existing planned session
# need clarity on cloning behavior time shift
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
+@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True)
+@user_passes_test(can_plan,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_teamclone_view(request,id=0):
@@ -1499,13 +1470,7 @@ def plannedsession_teamclone_view(request,id=0):
except IndexError:
trainingplan = None
- try:
- ps = PlannedSession.objects.get(id=id)
- except PlannedSession.DoesNotExist:
- raise Http404("Planned Session does not exist")
-
- if ps.manager != request.user:
- raise PermissionDenied("You are not allowed to clone this planned session")
+ ps = get_object_or_404(PlannedSession,pk=id)
ps.pk = None
@@ -1524,7 +1489,7 @@ def plannedsession_teamclone_view(request,id=0):
url = reverse(plannedsession_teamedit_view,
kwargs = {
- 'sessionid':ps.id,
+ 'id':ps.id,
'userid':r.user.id,
}
)
@@ -1536,7 +1501,8 @@ def plannedsession_teamclone_view(request,id=0):
next = request.GET.get('next', url)
return HttpResponseRedirect(next)
-@user_passes_test(hasplannedsessions, login_url="/rowers/paidplans/",
+@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True)
+@user_passes_test(can_plan, login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_templateedit_view(request,id=0):
@@ -1629,7 +1595,8 @@ def plannedsession_templateedit_view(request,id=0):
})
-@user_passes_test(hasplannedsessions, login_url="/rowers/paidplans/",
+@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True)
+@user_passes_test(can_plan, login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_totemplate_view(request,id=0):
@@ -1638,13 +1605,7 @@ def plannedsession_totemplate_view(request,id=0):
startdate, enddate = get_dates_timeperiod(request)
- try:
- ps = PlannedSession.objects.get(id=id)
- except PlannedSession.DoesNotExist:
- raise Http404("Planned Session does not exist")
-
- if ps.manager != request.user:
- raise PermissionDenied("You can only store your own sessions as a template")
+ ps = get_object_or_404(PlannedSession,pk=id)
ps.pk = None
ps.id = None
@@ -1664,7 +1625,8 @@ def plannedsession_totemplate_view(request,id=0):
return HttpResponseRedirect(next)
# Edit an existing planned session
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
+@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True)
+@user_passes_test(can_plan,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_edit_view(request,id=0,userid=0):
@@ -1684,21 +1646,12 @@ def plannedsession_edit_view(request,id=0,userid=0):
except IndexError:
trainingplan = None
- try:
- ps = PlannedSession.objects.get(id=id)
- except PlannedSession.DoesNotExist:
- raise Http404("Planned Session does not exist")
-
- if ps.manager != request.user:
- raise PermissionDenied("You are not allowed to edit this planned session")
-
- if ps.sessiontype in ['race','indoorrace']:
- raise PermissionDenied("You are not allowed to edit this planned session because it is a race")
+ ps = get_object_or_404(PlannedSession,pk=id)
if ps.team.all() or ps.rower.all().count()>1:
url = reverse(plannedsession_teamedit_view,
kwargs={
- 'sessionid':id,
+ 'id':id,
})
return HttpResponseRedirect(url)
@@ -1794,21 +1747,16 @@ def plannedsession_edit_view(request,id=0,userid=0):
})
-@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
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})
@@ -1816,16 +1764,12 @@ def plannedsession_detach_view(request,id=0,psid=0):
return HttpResponseRedirect(next)
@login_required()
+@permission_required('plannedsession.view_session',fn=get_session_by_pk,raise_exception=True)
def plannedsession_view(request,id=0,userid=0):
r = getrequestplanrower(request,userid=userid)
-
-
- try:
- ps = PlannedSession.objects.get(id=id)
- except PlannedSession.DoesNotExist:
- raise Http404("Planned Session does not exist")
+ ps = get_object_or_404(PlannedSession,pk=id)
if ps.sessiontype in ['race','indoorrace']:
url = reverse('virtualevent_view',
@@ -1842,16 +1786,6 @@ def plannedsession_view(request,id=0,userid=0):
m = ps.manager
mm = Rower.objects.get(user=m)
- if ps.manager != request.user:
- if 'coach' in r.rowerplan and r not in ps.rower.all():
- teams = Team.objects.filter(manager=request.user)
- members = Rower.objects.filter(team__in=teams).distinct()
- teamusers = [m.user for m in members]
- if ps.manager not in teamusers:
- raise PermissionDenied("You do not have access to this session")
- elif r not in ps.rower.all():
- raise PermissionDenied("You do not have access to this session")
-
resultsdict = get_session_metrics(ps)
resultsdict = pd.DataFrame(resultsdict).transpose().to_dict()
@@ -2055,24 +1989,23 @@ class PlannedSessionDelete(DeleteView):
w.save()
url = reverse(plannedsessions_view)
+ next = self.request.GET.get('next',url)
- return url
+ return next
def get_object(self, *args, **kwargs):
obj = super(PlannedSessionDelete, self).get_object(*args, **kwargs)
- m = Rower.objects.get(user=obj.manager)
- if not checkaccessuser(self.request.user,m):
+ if not can_delete_session(self.request.user,obj):
raise PermissionDenied('You are not allowed to delete this planned session')
return obj
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
+@user_passes_test(can_plan,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
-def rower_create_trainingplan(request,userid=0):
-
- therower = getrequestrower(request,userid=userid)
+def rower_create_trainingplan(request,id=0):
+ therower = getrequestrower(request,userid=id)
theuser = therower.user
themanager = getrower(request.user)
@@ -2138,7 +2071,8 @@ def rower_create_trainingplan(request,userid=0):
p.save()
for athlete in athletes:
- p.rowers.add(athlete)
+ if can_plan_user(request.user,athlete):
+ p.rowers.add(athlete)
targets = TrainingTarget.objects.filter(
@@ -2173,12 +2107,12 @@ def rower_create_trainingplan(request,userid=0):
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
- kwargs={'userid':userid}),
+ kwargs={'userid':id}),
'name': 'Plan'
},
{
'url':reverse(rower_create_trainingplan,
- kwargs={'userid':userid}),
+ kwargs={'id':id}),
'name': 'Manage Plans and Targets'
}
]
@@ -2196,40 +2130,27 @@ def rower_create_trainingplan(request,userid=0):
'old_targets':old_targets,
})
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
+@user_passes_test(can_plan,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
+@permission_required('target.delete_target',fn=get_target_by_pk,raise_exception=True)
def rower_delete_trainingtarget(request,id=0):
- try:
- target = TrainingTarget.objects.get(id=id)
- except TrainingPlan.DoesNotExist:
- raise Http404("Training Plan Does Not Exist")
- if checkaccessuser(request.user,target.manager):
- target.delete()
- messages.info(request,"We have deleted the training target")
- else:
- raise PermissionDenied("Access denied")
+ target = get_object_or_404(TrainingTarget,pk=id)
+ target.delete()
url = reverse(rower_create_trainingplan)
return HttpResponseRedirect(url)
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
+@user_passes_test(can_plan,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
+@permission_required('target.delete_plan',fn=get_plan_by_pk,raise_exception=True)
def rower_delete_trainingplan(request,id=0):
- try:
- plan = TrainingPlan.objects.get(id=id)
- except TrainingPlan.DoesNotExist:
- raise Http404("Training Plan Does Not Exist")
-
- if checkaccessuser(request.user,plan.manager):
- plan.delete()
- messages.info(request,"We have deleted the training plan")
- else:
- raise PermissionDenied("Access denied")
+ plan = get_object_or_404(TrainingPlan,pk=id)
+ plan.delete()
url = reverse(rower_create_trainingplan)
@@ -2242,7 +2163,7 @@ class TrainingPlanDelete(DeleteView):
def get_object(self, *args, **kwargs):
obj = super(TrainingPlanDelete, self).get_object(*args, **kwargs)
- if not checkaccessuser(self.request.user,obj.manager):
+ if not can_delete_plan(self.request.user,obj):
raise PermissionDenied('You are not allowed to delete this training plan')
return obj
@@ -2308,7 +2229,7 @@ class MicroCycleDelete(DeleteView):
def get_object(self, *args, **kwargs):
obj = super(MicroCycleDelete, self).get_object(*args, **kwargs)
- if not checkaccessuser(self.request.user,obj.plan.plan.plan.manager):
+ if not can_delete_cycle(self.request.user,obj):
raise PermissionDenied('You are not allowed to delete this training plan cycle')
return obj
@@ -2370,7 +2291,7 @@ class MesoCycleDelete(DeleteView):
def get_object(self, *args, **kwargs):
obj = super(MesoCycleDelete, self).get_object(*args, **kwargs)
- if not checkaccessuser(self.request.user,obj.plan.plan.manager):
+ if not can_delete_cycle(self.request.user,obj):
raise PermissionDenied('You are not allowed to delete this training plan cycle')
return obj
@@ -2424,13 +2345,13 @@ class MacroCycleDelete(DeleteView):
def get_object(self, *args, **kwargs):
obj = super(MacroCycleDelete, self).get_object(*args, **kwargs)
- if not checkaccessuser(self.request.user,obj.plan.manager):
+ if not can_delete_cycle(self.request.user,obj):
raise PermissionDenied('You are not allowed to delete this training plan cycle')
return obj
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
+@user_passes_test(can_plan,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def rower_trainingplan_execution_view(request,
@@ -2447,7 +2368,7 @@ def rower_trainingplan_execution_view(request,
plan = TrainingPlan.objects.get(id=id)
except TrainingPlan.DoesNotExist:
raise Http404("Training Plan Does Not Exist")
- if not checkaccessuser(request.user,plan.manager):
+ if not is_coach_user(request.user,plan.manager.user):
if request.user.rower not in plan.rowers.all():
raise PermissionDenied("Access denied")
@@ -2520,9 +2441,11 @@ def rower_trainingplan_execution_view(request,
)
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
- message="This functionality requires a Coach or Self-Coach plan",
- redirect_field_name=None)
+#@user_passes_test(can_plan,login_url="/rowers/paidplans",
+# message="This functionality requires a Coach or Self-Coach plan",
+# redirect_field_name=None)
+@login_required()
+@permission_required('plan.view_plan',fn=get_plan_by_pk,raise_exception=True)
def rower_trainingplan_view(request,
id=0,
userid=0,
@@ -2533,18 +2456,9 @@ def rower_trainingplan_view(request,
startdate,enddate = get_dates_timeperiod(request)
-
- try:
- plan = TrainingPlan.objects.get(id=id)
- except TrainingPlan.DoesNotExist:
- raise Http404("Training Plan Does Not Exist")
-
+ plan = get_object_or_404(TrainingPlan,pk=id)
r = getrequestrower(request,userid=userid)
- if not checkaccessuser(request.user,plan.manager):
- if request.user.rower not in plan.rowers.all():
- raise PermissionDenied("Access denied")
-
createmacrofillers(plan)
macrocycles = TrainingMacroCycle.objects.filter(
plan=plan,
@@ -2667,10 +2581,7 @@ class TrainingMacroCycleUpdate(UpdateView):
def get_object(self, *args, **kwargs):
obj = super(TrainingMacroCycleUpdate, self).get_object(*args, **kwargs)
- if obj.plan.manager is not None and self.request.user.rower != obj.plan.manager:
- raise PermissionDenied('You are not allowed to edit this training plan cycle')
-
- if not checkaccessuser(self.request.user,obj.plan.manager):
+ if not can_change_cycle(self.request.user,obj):
raise PermissionDenied('You are not allowed to edit this training plan cycle')
else:
obj.type = 'userdefined'
@@ -2741,7 +2652,7 @@ class TrainingMesoCycleUpdate(UpdateView):
def get_object(self, *args, **kwargs):
obj = super(TrainingMesoCycleUpdate, self).get_object(*args, **kwargs)
- if obj.plan.plan.manager is not None and self.request.user.rower != obj.plan.plan.manager:
+ if not can_change_cycle(self.request.user,obj):
raise PermissionDenied('You are not allowed to edit this training plan cycle')
else:
@@ -2819,7 +2730,7 @@ class TrainingMicroCycleUpdate(UpdateView):
def get_object(self, *args, **kwargs):
obj = super(TrainingMicroCycleUpdate, self).get_object(*args, **kwargs)
- if obj.plan.plan.plan.manager is not None and self.request.user.rower != obj.plan.plan.plan.manager:
+ if not can_change_cycle(self.request.user,obj):
raise PermissionDenied('You are not allowed to edit this training plan cycle')
@@ -2884,10 +2795,8 @@ class TrainingPlanUpdate(UpdateView):
def get_object(self, *args, **kwargs):
obj = super(TrainingPlanUpdate, self).get_object(*args, **kwargs)
- if obj.manager is not None and self.request.user.rower != obj.manager:
+ if not can_change_plan(self.request.user,obj):
raise PermissionDenied('You are not allowed to edit this training plan cycle')
- if obj.manager.rowerplan not in ['coach','freecoach','plan']:
- raise PermissionDenied('You are not allowed to edit this training plan')
return obj
@@ -2936,24 +2845,19 @@ class TrainingTargetUpdate(UpdateView):
def get_object(self, *args, **kwargs):
obj = super(TrainingTargetUpdate, self).get_object(*args, **kwargs)
- if obj.manager is not None and self.request.user.rower != obj.manager:
+ if not can_change_target(self.request.user,obj):
raise PermissionDenied('You are not allowed to edit this training plan cycle')
return obj
from rowers.utils import allsundays
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
+@user_passes_test(can_plan,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
+@permission_required('cycle.change_cycle',fn=get_meso_by_pk,raise_exception=True)
def planmesocyclebyweek(request,id=0,userid=0):
- try:
- cycle = TrainingMesoCycle.objects.get(id=id)
- except TrainingMesoCycle.DoesNotExist:
- raise Http404("Training Cycle does not exist")
-
- if not checkaccessuser(request.user,cycle.plan.plan.manager):
- raise PermissionDenied("You are not allowed to do this")
+ cycle = get_object_or_404(TrainingMesoCycle,pk=id)
micros = TrainingMicroCycle.objects.filter(plan=cycle)
for m in micros:
@@ -2998,17 +2902,12 @@ def planmesocyclebyweek(request,id=0,userid=0):
from rowers.utils import allmonths
-@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans",
+@user_passes_test(can_plan,login_url="/rowers/paidplans",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
+@permission_required('cycle.change_cycle',fn=get_macro_by_pk,raise_exception=True)
def planmacrocyclebymonth(request,id=0,userid=0):
- try:
- cycle = TrainingMacroCycle.objects.get(id=id)
- except TrainingMacroCycle.DoesNotExist:
- raise Http404("Training Cycle does not exist")
-
- if not checkaccessuser(request.user,cycle.plan.manager):
- raise PermissionDenied("You are not allowed to do this")
+ cycle = get_object_or_404(TrainingMacroCycle,pk=id)
mesos = TrainingMesoCycle.objects.filter(plan=cycle)
for m in mesos:
diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py
index 1cc53312..e9c7e3cf 100644
--- a/rowers/views/racesviews.py
+++ b/rowers/views/racesviews.py
@@ -7,7 +7,6 @@ from rowers.views.statements import *
# List Courses
-@login_required()
def courses_view(request):
r = getrower(request.user)
@@ -45,7 +44,7 @@ def course_map_view(request,id=0):
return Http404("Course doesn't exist")
script,div = course_map(course)
-
+
breadcrumbs = [
{
'url': reverse('virtualevents_view'),
@@ -64,10 +63,10 @@ def course_map_view(request,id=0):
'name': 'Map'
}
]
-
+
r = getrower(request.user)
-
-
+
+
return render(request,
'coursemap.html',
{
@@ -76,21 +75,16 @@ def course_map_view(request,id=0):
'mapscript':script,
'active':'nav-racing',
'rower':r,
- 'breadcrumbs':breadcrumbs,
+ 'breadcrumbs':breadcrumbs,
})
-
+
@login_required()
+@permission_required('course.change_course',fn=get_course_by_pk,raise_exception=True)
def course_replace_view(request,id=0):
- try:
- course = GeoCourse.objects.get(id=id)
- except GeoCourse.DoesNotExist:
- return Http404("Course doesn't exist")
+ course = get_object_or_404(GeoCourse,pk=id)
r = getrower(request.user)
-
- if course.manager != r:
- raise PermissionDenied("Access denied")
thecourses = GeoCourse.objects.filter(manager=r).exclude(id=id)
@@ -141,18 +135,13 @@ def course_replace_view(request,id=0):
'mapdiv':div,
'mapscript':script,
'form':form})
-
+
@login_required()
+@permission_required('course.delete_course',fn=get_course_by_pk,raise_exception=True)
def course_delete_view(request,id=0):
- try:
- course = GeoCourse.objects.get(id=id)
- except GeoCourse.DoesNotExist:
- return Http404("Course doesn't exist")
+ course = get_object_or_404(GeoCourse,pk=id)
r = getrower(request.user)
-
- if course.manager != r:
- raise PermissionDenied("Access denied")
ps = PlannedSession.objects.filter(course=course)
nosessions = len(ps) == 0
@@ -163,22 +152,17 @@ def course_delete_view(request,id=0):
url = reverse(courses_view)
return HttpResponseRedirect(url)
-
+
@login_required()
+@permission_required('course.change_course',fn=get_course_by_pk,raise_exception=True)
def course_edit_view(request,id=0):
- try:
- course = GeoCourse.objects.get(id=id)
- except GeoCourse.DoesNotExist:
- return Http404("Course doesn't exist")
+ course = get_object_or_404(GeoCourse,pk=id)
r = getrower(request.user)
-
- if course.manager != r:
- raise PermissionDenied("Access denied")
ps = PlannedSession.objects.filter(course=course)
nosessions = len(ps) == 0
-
+
script,div = course_map(course)
if request.method == 'POST':
@@ -187,7 +171,7 @@ def course_edit_view(request,id=0):
name = form.cleaned_data['name']
country = form.cleaned_data['country']
notes = form.cleaned_data['notes']
-
+
course.name = name
course.country = country
course.notes = notes
@@ -213,7 +197,7 @@ def course_edit_view(request,id=0):
'name': 'Edit'
}
]
-
+
return render(request, 'course_edit_view.html',
{
'course':course,
@@ -235,9 +219,9 @@ def course_view(request,id=0):
return Http404("Course doesn't exist")
r = getrower(request.user)
-
+
script,div = course_map(course)
-
+
breadcrumbs = [
{
'url': reverse('virtualevents_view'),
@@ -266,11 +250,10 @@ def course_view(request,id=0):
)
@login_required()
+@permission_required('racelogo.delete_logo',fn=get_logo_by_pk,raise_exception=True)
def logo_delete_view(request,id=0):
- try:
- logo = RaceLogo.objects.get(id=id)
- except RaceLogo.DoesNotExist:
- raise Http404("Logo doesn't exist")
+ logo = get_object_or_404(RaceLogo,pk=id)
+
if logo.user == request.user:
logo.delete()
@@ -281,28 +264,18 @@ def logo_delete_view(request,id=0):
return HttpResponseRedirect(url)
@login_required()
+@permission_required('virtualevent.change_race',fn=get_virtualevent_by_pk,raise_exception=True)
def virtualevent_setlogo_view(request,id=0,logoid=0):
- try:
- race = VirtualRace.objects.get(id=id)
- except VirtualRace.DoesNotExist:
- raise Http404("Race doesn't exist")
+ race = get_object_or_404(VirtualRace,pk=id)
+ logo = get_object_or_404(RaceLogo,pk=logoid)
- try:
- logo = RaceLogo.objects.get(id=logoid)
- except RaceLogo.DoesNotExist:
- raise Http404("Logo doesn't exist")
+ otherlogos = race.logos.all()
+ for otherlogo in otherlogos:
+ otherlogo.race.remove(race)
- if logo.user == request.user and race.manager == request.user:
- otherlogos = race.logos.all()
- for otherlogo in otherlogos:
- otherlogo.race.remove(race)
-
- logo.race.add(race)
- logo.save()
- else:
- message = "You do not own this race or this image"
- messages.error(request,message)
+ logo.race.add(race)
+ logo.save()
url = reverse('virtualevent_view',
kwargs={'id':id})
@@ -318,10 +291,7 @@ def virtualevent_uploadimage_view(request,id=0):
r = getrower(request.user)
- try:
- race = VirtualRace.objects.get(id=id)
- except VirtualRace.DoesNotExist:
- raise Http404("Race doesn't exist")
+ race = get_object_or_404(VirtualRace,pk=id)
logos = RaceLogo.objects.filter(user=request.user).order_by("-creationdatetime")
@@ -348,7 +318,7 @@ def virtualevent_uploadimage_view(request,id=0):
kwargs={'id':id})
return HttpResponseRedirect(url)
-
+
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
f = form.cleaned_data['file']
@@ -399,14 +369,14 @@ def virtualevent_uploadimage_view(request,id=0):
else:
return HttpResponseRedirect(url)
else:
-
+
form = ImageForm()
if is_ajax:
return {'result':0}
-
+
return render(request,'logo_form.html',
{'form':form,
'rower':r,
@@ -425,7 +395,7 @@ def course_upload_view(request):
is_ajax = True
r = getrower(request.user)
-
+
if request.method == 'POST':
form = CourseForm(request.POST,request.FILES)
@@ -435,7 +405,7 @@ def course_upload_view(request):
notes = form.cleaned_data['notes']
if f is not None:
filename,path_and_filename = handle_uploaded_file(f)
-
+
cs = courses.kmltocourse(path_and_filename)
@@ -443,11 +413,11 @@ def course_upload_view(request):
cname = name+' - '+course['name']
cnotes = notes+'\n\n'+course['description']
polygons = course['polygons']
-
+
course = courses.createcourse(r,cname,polygons,notes=cnotes)
-
+
os.remove(path_and_filename)
-
+
url = reverse(courses_view)
if is_ajax:
return JSONResponse({'result':1,'url':url})
@@ -492,7 +462,7 @@ def virtualevents_view(request):
evaluation_closure__gte=timezone.now()-datetime.timedelta(days=3),
)
-
+
races = (races1 | races2).order_by("startdate","start_time")
if not request.user.is_anonymous:
@@ -524,7 +494,7 @@ def virtualevents_view(request):
country__in=countries
)
-
+
races = (races1 | races2).order_by("startdate","start_time")
@@ -550,7 +520,7 @@ def virtualevents_view(request):
country__in=countries
).order_by("startdate","start_time")
else:
-
+
form = VirtualRaceSelectForm()
if is_ajax:
@@ -558,7 +528,7 @@ def virtualevents_view(request):
{ 'races':races,
'rower':r,
})
-
+
breadcrumbs = [
{
'url':reverse('virtualevents_view'),
@@ -576,18 +546,13 @@ def virtualevents_view(request):
)
@login_required()
-def virtualevent_disqualify_view(request,raceid=0,recordid=0):
+@permission_required('virtualevent.change_race',fn=get_virtualevent_by_pk,raise_exception=True)
+def virtualevent_disqualify_view(request,id=0,recordid=0):
r = getrower(request.user)
+ race = get_object_or_404(VirtualRace,pk=id)
+
- try:
- race = VirtualRace.objects.get(id=raceid)
- except VirtualRace.DoesNotExist:
- raise Http404("Virtual Race does not exist")
-
- if r.user != race.manager:
- raise PermissionDenied("Access denied")
-
if race.sessiontype == 'race':
recordobj = VirtualRaceResult
else:
@@ -603,7 +568,7 @@ def virtualevent_disqualify_view(request,raceid=0,recordid=0):
if timezone.now() > race.evaluation_closure+datetime.timedelta(hours=1):
messages.error(request,"The evaluation is already closed and the results are official")
url = reverse('virtualevent_view',kwargs={'id':raceid})
-
+
return HttpResponseRedirect(url)
if request.method == 'POST':
@@ -619,16 +584,16 @@ def virtualevent_disqualify_view(request,raceid=0,recordid=0):
job = myqueue(queue,handle_send_disqualification_email,
r.user.email, name,
disqualifier,message,race.name)
-
+
messages.info(request,"We have invalidated the result for: "+str(record))
-
+
record.coursecompleted = False
record.save()
url = reverse('virtualevent_view',kwargs={'id':raceid})
return HttpResponseRedirect(url)
-
+
else:
form = DisqualificationForm(request.POST)
@@ -705,7 +670,7 @@ def virtualevent_disqualify_view(request,raceid=0,recordid=0):
if race_can_edit(r,race):
buttons += ['editbutton']
-
+
return render(request,"disqualification_view.html",
{'workout':workout,
'active':'nav-racing',
@@ -723,13 +688,13 @@ def virtualevent_disqualify_view(request,raceid=0,recordid=0):
def virtualevent_view(request,id=0):
results = []
-
+
if not request.user.is_anonymous:
r = getrower(request.user)
else:
r = None
-
+
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
@@ -744,8 +709,8 @@ def virtualevent_view(request,id=0):
resultobj = IndoorVirtualRaceResult
records = resultobj.objects.filter(race=race)
-
-
+
+
buttons = []
# to-do - add DNS
@@ -755,7 +720,7 @@ def virtualevent_view(request,id=0):
race=race,
workoutid__isnull=True,
)
-
+
if not request.user.is_anonymous:
if race_can_register(r,race):
@@ -790,14 +755,14 @@ def virtualevent_view(request,id=0):
except KeyError:
boattype = mytypes.waterboattype
- try:
+ try:
boatclass = cd['boatclass']
except KeyError:
if race.sessiontype == 'race':
boatclass = [t for t in mytypes.otwtypes]
else:
boatclass = [t for t in mytypes.otetypes]
-
+
age_min = cd['age_min']
age_max = cd['age_max']
@@ -834,8 +799,8 @@ def virtualevent_view(request,id=0):
age__gte=age_min,
age__lte=age_max
).order_by("duration","-distance")
-
-
+
+
# to-do - add DNS
dns = []
if timezone.now() > race.evaluation_closure:
@@ -860,9 +825,9 @@ def virtualevent_view(request,id=0):
form = RaceResultFilterForm(records=records)
else:
form = None
-
-
+
+
breadcrumbs = [
{
'url':reverse('virtualevents_view'),
@@ -875,7 +840,7 @@ def virtualevent_view(request,id=0):
'name': race.name
}
]
-
+
racelogos = race.logos.all()
if racelogos:
@@ -885,7 +850,7 @@ def virtualevent_view(request,id=0):
comments = PlannedSessionComment.objects.filter(plannedsession=race).order_by("created")
-
+
return render(request,'virtualevent.html',
{
'coursescript':script,
@@ -906,13 +871,13 @@ def virtualevent_view(request,id=0):
def virtualevent_ranking_view(request,id=0):
results = []
-
+
if not request.user.is_anonymous:
r = getrower(request.user)
else:
r = None
-
+
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
@@ -927,8 +892,8 @@ def virtualevent_ranking_view(request,id=0):
resultobj = IndoorVirtualRaceResult
records = resultobj.objects.filter(race=race)
-
-
+
+
buttons = []
# to-do - add DNS
@@ -938,7 +903,7 @@ def virtualevent_ranking_view(request,id=0):
race=race,
workoutid__isnull=True,
)
-
+
if not request.user.is_anonymous:
if race_can_register(r,race):
@@ -973,14 +938,14 @@ def virtualevent_ranking_view(request,id=0):
except KeyError:
boattype = mytypes.waterboattype
- try:
+ try:
boatclass = cd['boatclass']
except KeyError:
if race.sessiontype == 'race':
boatclass = [t for t in mytypes.otwtypes]
else:
boatclass = [t for t in mytypes.otetypes]
-
+
age_min = cd['age_min']
age_max = cd['age_max']
@@ -1017,8 +982,8 @@ def virtualevent_ranking_view(request,id=0):
age__gte=age_min,
age__lte=age_max
).order_by("duration","-distance")
-
-
+
+
# to-do - add DNS
dns = []
if timezone.now() > race.evaluation_closure:
@@ -1043,9 +1008,9 @@ def virtualevent_ranking_view(request,id=0):
form = RaceResultFilterForm(records=records)
else:
form = None
-
-
+
+
breadcrumbs = [
{
'url':reverse('virtualevents_view'),
@@ -1058,7 +1023,7 @@ def virtualevent_ranking_view(request,id=0):
'name': race.name
}
]
-
+
racelogos = race.logos.all()
if racelogos:
@@ -1132,7 +1097,7 @@ def virtualevent_addboat_view(request,id=0):
boattypes = [record.boattype for record in records]
boatclasses = [record.boatclass for record in records]
allowedboats = tuple([ type for type in mytypes.boattypes if type[0] not in boattypes] )
-
+
# we're still here
if request.method == 'POST':
@@ -1151,14 +1116,14 @@ def virtualevent_addboat_view(request,id=0):
sex = r.sex
if mix:
sex = 'mixed'
-
+
if boattype == '1x' and r.birthdate:
age = calculate_age(r.birthdate)
sex = r.sex
if sex == 'not specified':
sex = 'male'
-
+
if boattype in boattypes and boatclass in boatclasses:
# check if different sexes
therecords = records.filter(
@@ -1167,7 +1132,7 @@ def virtualevent_addboat_view(request,id=0):
thesexes = [record.sex for record in therecords]
if sex in thesexes:
-
+
messages.error(
request,
"You have already registered in that boat class/type"
@@ -1199,11 +1164,11 @@ def virtualevent_addboat_view(request,id=0):
)
record.save()
-
+
add_rower_race(r,race)
-
-
+
+
messages.info(
request,
"You have successfully registered for this race. Good luck!"
@@ -1215,14 +1180,14 @@ def virtualevent_addboat_view(request,id=0):
})
return HttpResponseRedirect(url)
-
+
else:
initial = {
'age': calculate_age(r.birthdate),
'weightcategory': r.weightcategory,
'adaptiveclass': r.adaptiveclass,
}
-
+
form = VirtualRaceResultForm(initial=initial)
breadcrumbs = [
@@ -1243,8 +1208,8 @@ def virtualevent_addboat_view(request,id=0):
'name': 'Add Discipline'
}
]
-
-
+
+
buttons = []
if not request.user.is_anonymous:
@@ -1311,7 +1276,7 @@ def virtualevent_register_view(request,id=0):
sex = r.sex
if mix:
sex = 'mixed'
-
+
if boattype == '1x' and r.birthdate:
age = calculate_age(r.birthdate)
sex = r.sex
@@ -1338,7 +1303,7 @@ def virtualevent_register_view(request,id=0):
)
record.save()
-
+
add_rower_race(r,race)
otherrecords = IndoorVirtualRaceResult.objects.filter(
@@ -1357,8 +1322,8 @@ def virtualevent_register_view(request,id=0):
race.name,
race.id
)
-
-
+
+
messages.info(
request,
"You have successfully registered for this race. Good luck!"
@@ -1370,14 +1335,14 @@ def virtualevent_register_view(request,id=0):
})
return HttpResponseRedirect(url)
-
+
else:
initial = {
'age': calculate_age(r.birthdate),
'weightcategory': r.weightcategory,
'adaptiveclass': r.adaptiveclass,
}
-
+
form = VirtualRaceResultForm(initial=initial)
breadcrumbs = [
@@ -1419,7 +1384,7 @@ def virtualevent_register_view(request,id=0):
if race_can_edit(r,race):
buttons += ['editbutton']
-
+
return render(request,'virtualeventregister.html',
{
'form':form,
@@ -1440,7 +1405,7 @@ def virtualevent_toggle_email_view(request,id=0):
newsetting = False
else:
newsetting = True
-
+
for record in records:
record.emailnotifications = newsetting
record.save()
@@ -1454,7 +1419,7 @@ def virtualevent_toggle_email_view(request,id=0):
def indoorvirtualevent_toggle_email_view(request,id=0):
r = getrower(request.user)
race = VirtualRace.objects.get(id=id)
-
+
records = IndoorVirtualRaceResult.objects.filter(userid=r.id,
race=race)
@@ -1462,7 +1427,7 @@ def indoorvirtualevent_toggle_email_view(request,id=0):
newsetting = False
else:
newsetting = True
-
+
for record in records:
record.emailnotifications = newsetting
record.save()
@@ -1503,7 +1468,7 @@ def indoorvirtualevent_register_view(request,id=0):
boatclass = cd['boatclass']
sex = r.sex
-
+
if r.birthdate:
age = calculate_age(r.birthdate)
sex = r.sex
@@ -1529,7 +1494,7 @@ def indoorvirtualevent_register_view(request,id=0):
)
record.save()
-
+
add_rower_race(r,race)
otherrecords = IndoorVirtualRaceResult.objects.filter(
@@ -1548,8 +1513,8 @@ def indoorvirtualevent_register_view(request,id=0):
race.name,
race.id
)
-
-
+
+
messages.info(
request,
"You have successfully registered for this race. Good luck!"
@@ -1561,14 +1526,14 @@ def indoorvirtualevent_register_view(request,id=0):
})
return HttpResponseRedirect(url)
-
+
else:
initial = {
'age': calculate_age(r.birthdate),
'weightcategory': r.weightcategory,
'adaptiveclass': r.adaptiveclass,
}
-
+
form = IndoorVirtualRaceResultForm(initial=initial)
breadcrumbs = [
@@ -1624,7 +1589,7 @@ def indoorvirtualevent_register_view(request,id=0):
@login_required()
def indoorvirtualevent_create_view(request):
r = getrower(request.user)
-
+
if request.method == 'POST':
racecreateform = IndoorVirtualRaceForm(request.POST)
if racecreateform.is_valid():
@@ -1650,7 +1615,7 @@ def indoorvirtualevent_create_view(request):
startdatetime = datetime.datetime.combine(startdate,start_time)
enddatetime = datetime.datetime.combine(enddate,end_time)
-
+
startdatetime = pytz.timezone(timezone_str).localize(
startdatetime
)
@@ -1681,7 +1646,7 @@ def indoorvirtualevent_create_view(request):
sessionmode = 'time'
else:
sessionmode = 'distance'
-
+
vs = VirtualRace(
name=name,
startdate=startdate,
@@ -1719,7 +1684,7 @@ def indoorvirtualevent_create_view(request):
),
dotweet = dotweet
)
-
+
sa.save()
url = reverse('virtualevents_view')
@@ -1728,7 +1693,7 @@ def indoorvirtualevent_create_view(request):
racecreateform = IndoorVirtualRaceForm(timezone=r.defaulttimezone)
-
+
breadcrumbs = [
{
'url':reverse('virtualevents_view'),
@@ -1753,7 +1718,7 @@ def indoorvirtualevent_create_view(request):
@login_required()
def virtualevent_create_view(request):
r = getrower(request.user)
-
+
if request.method == 'POST':
racecreateform = VirtualRaceForm(request.POST)
if racecreateform.is_valid():
@@ -1779,7 +1744,7 @@ def virtualevent_create_view(request):
startdatetime = datetime.datetime.combine(startdate,start_time)
enddatetime = datetime.datetime.combine(enddate,end_time)
-
+
startdatetime = pytz.timezone(timezone_str).localize(
startdatetime
)
@@ -1805,8 +1770,8 @@ def virtualevent_create_view(request):
registration_closure = enddatetime
else:
registration_closure = evaluation_closure
-
-
+
+
vs = VirtualRace(
name=name,
startdate=startdate,
@@ -1842,7 +1807,7 @@ def virtualevent_create_view(request):
),
dotweet = dotweet
)
-
+
sa.save()
url = reverse('virtualevents_view')
return HttpResponseRedirect(url)
@@ -1850,7 +1815,7 @@ def virtualevent_create_view(request):
racecreateform = VirtualRaceForm()
-
+
breadcrumbs = [
{
'url':reverse('virtualevents_view'),
@@ -1872,15 +1837,12 @@ def virtualevent_create_view(request):
})
@login_required()
+@permission_required('virtualevent.change_race',fn=get_virtualevent_by_pk,raise_exception=True)
def virtualevent_edit_view(request,id=0):
r = getrower(request.user)
-
- try:
- race = VirtualRace.objects.get(id=id)
- if race.manager != request.user:
- raise PermissionDenied("Access denied")
- except VirtualRace.DoesNotExist:
- raise Http404("Virtual Race does not exist")
+ race = get_object_or_404(VirtualRace,pk=id)
+
+
start_time = race.start_time
start_date = race.startdate
@@ -1900,7 +1862,7 @@ def virtualevent_edit_view(request,id=0):
racecreateform = VirtualRaceForm(request.POST,instance=race)
if racecreateform.is_valid():
cd = racecreateform.cleaned_data
-
+
res, message = update_virtualrace(race,cd)
if res:
@@ -1912,7 +1874,7 @@ def virtualevent_edit_view(request,id=0):
kwargs = {
'id':race.id
})
-
+
return HttpResponseRedirect(url)
else:
@@ -1937,7 +1899,7 @@ def virtualevent_edit_view(request,id=0):
'name': 'Edit'
}
]
-
+
buttons = []
if not request.user.is_anonymous:
@@ -1970,15 +1932,10 @@ def virtualevent_edit_view(request,id=0):
})
@login_required()
+@permission_required('virtualevent.change_race',fn=get_virtualevent_by_pk,raise_exception=True)
def indoorvirtualevent_edit_view(request,id=0):
r = getrower(request.user)
-
- try:
- race = VirtualRace.objects.get(id=id)
- if race.manager != request.user:
- raise PermissionDenied("Access denied")
- except VirtualRace.DoesNotExist:
- raise Http404("Virtual Race does not exist")
+ race = get_object_or_404(VirtualRace,pk=id)
start_time = race.start_time
start_date = race.startdate
@@ -1998,7 +1955,7 @@ def indoorvirtualevent_edit_view(request,id=0):
racecreateform = IndoorVirtualRaceForm(request.POST,instance=race)
if racecreateform.is_valid():
cd = racecreateform.cleaned_data
-
+
res, message = update_indoorvirtualrace(race,cd)
if res:
@@ -2010,14 +1967,14 @@ def indoorvirtualevent_edit_view(request,id=0):
kwargs = {
'id':race.id
})
-
+
return HttpResponseRedirect(url)
else:
racecreateform = IndoorVirtualRaceForm(instance=race)
-
+
breadcrumbs = [
{
'url':reverse('virtualevents_view'),
@@ -2057,8 +2014,8 @@ def indoorvirtualevent_edit_view(request,id=0):
if race_can_edit(r,race):
buttons += ['editbutton']
-
-
+
+
return render(request,'virtualeventedit.html',
{
'form':racecreateform,
@@ -2084,7 +2041,7 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
start_date = race.startdate
startdatetime = datetime.datetime.combine(start_date, start_time)
startdatetime = pytz.timezone(race.timezone).localize(startdatetime)
-
+
end_time = race.end_time
end_date = race.enddate
enddatetime = datetime.datetime.combine(end_date, end_time)
@@ -2096,7 +2053,7 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
resultobj = VirtualRaceResult
else:
resultobj = IndoorVirtualRaceResult
-
+
records = resultobj.objects.filter(
userid = r.id,
race=race
@@ -2104,7 +2061,7 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
entrychoices = []
-
+
for record in records:
rtpl = (record.id, record.__str__())
entrychoices.append(rtpl)
@@ -2121,7 +2078,7 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
}
)
return HttpResponseRedirect(url)
-
+
ws = Workout.objects.filter(
user=r,
workouttype__in=mytypes.rowtypes,
@@ -2134,14 +2091,14 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
request,
'You have no workouts executed during the race window. Please upload a result or enter it manually.'
)
-
+
url = reverse('virtualevent_view',
kwargs = {
'id':id
})
return HttpResponseRedirect(url)
-
+
initialworkouts = [w.id for w in Workout.objects.filter(
user=r,plannedsession=race
@@ -2151,7 +2108,7 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
workoutdata['initial'] = []
choices = []
-
+
for w in ws:
wtpl = (w.id, w.__str__())
choices.append(wtpl)
@@ -2183,7 +2140,7 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
result,comments,errors,jobid = add_workout_indoorrace(
workouts,race,r,recordid=recordid)
-
+
for c in comments:
messages.info(request,c)
for er in errors:
@@ -2217,13 +2174,13 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
race.id
)
-
+
# redirect to race page
url = reverse('virtualevent_view',
kwargs = {
'id':race.id
})
-
+
return HttpResponseRedirect(url)
else:
@@ -2273,7 +2230,7 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
if race_can_edit(r,race):
buttons += ['editbutton']
-
+
return render(request,'race_submit.html',
{
'race':race,
diff --git a/rowers/views/statements.py b/rowers/views/statements.py
index 25a27c9c..3bdac2cb 100644
--- a/rowers/views/statements.py
+++ b/rowers/views/statements.py
@@ -19,6 +19,7 @@ from django.db.models import Q
from django import template
from django.db import IntegrityError, transaction
from django.views.decorators.csrf import csrf_exempt
+from django.shortcuts import get_object_or_404
from matplotlib.backends.backend_agg import FigureCanvas
import gc
from pyparsing import ParseException
@@ -31,10 +32,22 @@ 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,isplanmember,can_delete_session,
+ can_view_target,can_change_target,can_delete_target,
+ can_view_plan,can_change_plan,can_delete_plan,
+ can_view_cycle,can_change_cycle,can_delete_cycle,
+ can_add_workout_member,can_plan_user,is_paid_coach,
+ can_start_trial, can_start_plantrial,can_plan,is_workout_team
+ )
+
from django.shortcuts import render
from django.template.loader import render_to_string
@@ -97,7 +110,7 @@ from rowers.models import (
TrainingMesoCycleForm, TrainingMicroCycleForm,
RaceLogo,RowerBillingAddressForm,PaidPlan,
AlertEditForm, ConditionEditForm,
- PlannedSessionComment,CoachRequest,CoachOffer,checkaccessplanuser,
+ PlannedSessionComment,CoachRequest,CoachOffer,
VideoAnalysis
)
from rowers.models import (
@@ -240,7 +253,7 @@ from rq import Queue,cancel_job
from django.core.cache import cache
from django_mailbox.models import Message,Mailbox,MessageAttachment
-
+from rules.contrib.views import permission_required, objectgetter
# Utility to get stroke data in a JSON response
class JSONResponse(HttpResponse):
@@ -274,6 +287,49 @@ def getfavorites(r,row):
return favorites,maxfav
+def get_logo_by_pk(request,*args,**kwargs):
+ id = kwargs['id']
+ return get_object_or_404(RaceLogo,pk=id)
+
+def get_virtualevent_by_pk(request,*args,**kwargs):
+ id = kwargs['id']
+ return get_object_or_404(VirtualRace,pk=id)
+
+def get_promember(request,*args,**kwargs):
+ return request.user
+
+def get_course_by_pk(request,*args,**kwargs):
+ id = kwargs['id']
+ return get_object_or_404(GeoCourse,pk=id)
+
+def get_workout_by_opaqueid(request,id,**kwargs):
+ pk = encoder.decode_hex(id)
+ return get_object_or_404(Workout,pk=pk)
+
+def get_session_by_pk(request,*args,**kwargs):
+ id = kwargs['id']
+ return get_object_or_404(PlannedSession,pk=id)
+
+def get_target_by_pk(request,*args,**kwargs):
+ id = kwargs['id']
+ return get_object_or_404(TrainingTarget,pk=id)
+
+def get_plan_by_pk(request,*args,**kwargs):
+ id = kwargs['id']
+ return get_object_or_404(TrainingPlan,pk=id)
+
+def get_macro_by_pk(request,*args,**kwargs):
+ id = kwargs['id']
+ return get_object_or_404(TrainingMacroCycle,pk=id)
+
+def get_meso_by_pk(request,*args,**kwargs):
+ id = kwargs['id']
+ return get_object_or_404(TrainingMesoCycle,pk=id)
+
+def get_micro_by_pk(request,*args,**kwargs):
+ id = kwargs['id']
+ return get_object_or_404(TrainingMicroCycle,pk=id)
+
def get_workout_default_page(request,id):
if request.user.is_anonymous:
return reverse('workout_view',kwargs={'id':id})
@@ -284,11 +340,42 @@ def get_workout_default_page(request,id):
else:
return reverse('workout_workflow_view',kwargs={'id':id})
-def getrequestrower(request,rowerid=0,userid=0,notpermanent=False):
+def get_user_by_userid(*args,**kwargs):
+ request = args[0]
+ try:
+ id = kwargs['userid']
+ except KeyError:
+ id = request.user.id
+ if id is not None and int(id) == 0:
+ id = request.user.id
+
+ u = get_object_or_404(User,pk=id)
+ return u
+
+def get_user_by_id(*args,**kwargs):
+ request = args[0]
+ try:
+ id = args[1]
+ except IndexError:
+ try:
+ id = kwargs['id']
+ except KeyError:
+ id = request.user.id
+
+ return get_object_or_404(User,pk=id)
+
+def get_rower_by_id(request,id):
+ u = User.objects.get(id=id)
+ return u.rower
+
+def getrequestrower(request,rowerid=0,userid=0,notpermanent=False):
userid = int(userid)
rowerid = int(rowerid)
+ if userid == 0:
+ userid = request.user.id
+
if notpermanent == False:
if rowerid == 0 and 'rowerid' in request.session:
rowerid = request.session['rowerid']
@@ -300,16 +387,25 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False):
if rowerid != 0:
r = Rower.objects.get(id=rowerid)
+ u = r.user
elif userid != 0:
u = User.objects.get(id=userid)
r = getrower(u)
+ elif request.user.is_anonymous:
+ return None
else:
r = getrower(request.user)
+ u = r.user
+
except Rower.DoesNotExist:
raise Http404("Rower doesn't exist")
- if not checkaccessuser(request.user,r):
+ if r.user == request.user:
+ return r
+
+ if userid != 0 and not is_rower_team_member(request.user,u.rower):
+ request.session['rowerid'] = request.user.rower.id
raise PermissionDenied("You have no access to this user")
if notpermanent == False:
@@ -342,7 +438,8 @@ 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 r.user != request.user and not can_plan_user(request.user,r ):
+ request.session['rowerid'] = r.id
raise PermissionDenied("You have no access to this user")
if notpermanent == False:
@@ -376,21 +473,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
@@ -432,7 +514,6 @@ class SessionTaskListener(threading.Thread):
for item in self.pubsub.listen():
if item['data'] == "KILL":
self.pubsub.unsubscribe()
- print(self, "unsubscribed and finished")
break
else:
self.work(item)
@@ -773,9 +854,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)
@@ -860,27 +941,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)
@@ -923,10 +983,10 @@ def rowhascoordinates(row):
def rdata(file,rower=rrower()):
try:
res = rrdata(csvfile=file,rower=rower)
- except (IOError, IndexError, EOFError):
+ except (IOError, IndexError, EOFError,FileNotFoundError):
try:
res = rrdata(csvfile=file+'.gz',rower=rower)
- except (IOError, IndexError, EOFError):
+ except (IOError, IndexError, EOFError,FileNotFoundError):
res = 0
return res
@@ -989,10 +1049,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:
@@ -1008,56 +1064,9 @@ def iscoachmember(user):
return result
-def cancreateteam(user):
- if user.is_anonymous:
- return False
-
- try:
- r = Rower.objects.get(user=user)
- except Rower.DoesNotExist:
- r = Rower(user=user)
- r.save()
-
- if user.is_authenticated and ('coach' in r.rowerplan):
- return True
- elif user.is_athenticated() and r.rowerplan in ['plan','pro']:
- ts = Team.objects.filter(manager=user)
- if len(otherteams) >= 1:
- return False
-
-# Check if a user can create planned sessions
-def hasplannedsessions(user):
- if 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 (r.rowerplan=='coach' or r.rowerplan=='freecoach' or r.rowerplan=='plan')
- if not result and r.plantrialexpires:
- result = user.is_authenticated and r.plantrialexpires >= datetime.date.today()
- else:
- result = False
-
- 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/teamviews.py b/rowers/views/teamviews.py
index 36c6b85b..16a8cc60 100644
--- a/rowers/views/teamviews.py
+++ b/rowers/views/teamviews.py
@@ -5,9 +5,16 @@ from __future__ import unicode_literals
from rowers.views.statements import *
+from rowers.rower_rules import *
+
+def get_team_by_pk(request,team_id):
+ return get_object_or_404(Team,pk=team_id)
+
+
@login_required()
-def team_view(request,id=0,userid=0):
+@permission_required('teams.view_team',fn=get_team_by_pk,raise_exception=True)
+def team_view(request,team_id=0,userid=0):
ismember = 0
hasrequested = 0
r = getrequestrower(request,userid=userid)
@@ -15,26 +22,21 @@ def team_view(request,id=0,userid=0):
myteams, memberteams, otherteams = get_teams(request)
teams.remove_expired_invites()
+ t = get_object_or_404(Team,pk=team_id)
-
- try:
- t = Team.objects.get(id=id)
- except Team.DoesNotExist:
- raise Http404("Team doesn't exist")
+ #if r.rowerplan == 'basic' and t.manager.rower.rowerplan != 'coach':
+ # raise PermissionDenied("You need to be on a Paid Plan to see or join this team")
- if r.rowerplan == 'basic' and t.manager.rower.rowerplan != 'coach':
- raise PermissionDenied("You need to be on a Paid Plan to see or join this team")
-
q = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct().exclude(rower__team__name=t.name)
mygroups = [request.user.rower.mycoachgroup]
q2 = User.objects.filter(rower__isnull=False,rower__coachinggroups__in=mygroups).distinct().exclude(rower__team__name=t.name)
-
+
q = q | q2
-
+
if request.method == 'POST' and request.user == t.manager and 'email' in request.POST:
inviteform = TeamInviteForm(request.POST)
-
+
inviteform.fields['user'].queryset = q
if inviteform.is_valid():
cd = inviteform.cleaned_data
@@ -59,7 +61,7 @@ def team_view(request,id=0,userid=0):
teams.send_team_message(t,message)
messages.info(request,'Message was sent to all team members')
groupmessageform = TeamMessageForm()
-
+
elif request.user == t.manager:
inviteform = TeamInviteForm()
inviteform.fields['user'].queryset = q
@@ -68,7 +70,7 @@ def team_view(request,id=0,userid=0):
inviteform = ''
groupmessageform = ''
-
+
members = Rower.objects.filter(team=t).order_by('user__last_name','user__first_name')
thisteammyrequests = TeamRequest.objects.filter(team=t,user=request.user)
if len(thisteammyrequests):
@@ -83,7 +85,7 @@ def team_view(request,id=0,userid=0):
'name': 'Groups'
},
{
- 'url':reverse(team_view,kwargs={'id':id}),
+ 'url':reverse(team_view,kwargs={'team_id':team_id}),
'name': t.name
}
]
@@ -112,14 +114,14 @@ def team_leaveconfirm_view(request,id=0):
raise Http404("Team doesn't exist")
myteams, memberteams, otherteams = get_teams(request)
-
+
breadcrumbs = [
{
'url':reverse(rower_teams_view),
'name': 'Groups'
},
{
- 'url':reverse(team_view,kwargs={'id':id}),
+ 'url':reverse(team_view,kwargs={'team_id':id}),
'name': t.name
},
{
@@ -144,7 +146,7 @@ def rower_calcdps_view(request):
ws = [(w.id,w.csvfilename) for w in Workout.objects.filter(user=r)]
res = myqueue(queue,handle_updatedps,r.user.email,ws,debug=False,
- emailbounced=r.emailbounced)
+ emailbounced=r.emailbounced)
messages.info(request,"Your workouts are being updated in the background. You will receive email when this is done.")
@@ -155,7 +157,7 @@ def rower_calcdps_view(request):
def team_leave_view(request,id=0):
r = getrower(request.user)
teams.remove_member(id,r)
-
+
url = reverse(rower_teams_view)
response = HttpResponseRedirect(url)
return response
@@ -164,7 +166,7 @@ from rowers.forms import TeamInviteCodeForm
def get_teams(request):
r = Rower.objects.get(user=request.user)
-
+
myteams = Team.objects.filter(
manager=request.user).order_by('name')
memberteams = Team.objects.filter(
@@ -191,7 +193,7 @@ def rower_teams_view(request):
messages.error(request,text)
else:
form = TeamInviteCodeForm()
-
+
r = getrower(request.user)
ts = Team.objects.filter(rower=r)
@@ -208,7 +210,7 @@ def rower_teams_view(request):
mycoachoffers = CoachOffer.objects.filter(coach=r)
# user is invited (by coach)
coachoffers = CoachOffer.objects.filter(user=r.user)
-
+
# user requests a coach
mycoachrequests = CoachRequest.objects.filter(user=r.user)
# user is requested to coach
@@ -230,7 +232,7 @@ def rower_teams_view(request):
potentialcoaches = [c for c in potentialcoaches if c.rower not in invitedcoaches+coaches]
potentialcoaches = [c for c in potentialcoaches if teams.get_coach_club_size(c.rower) datetime.date(1970,1,1):
+ if not can_start_trial(request.user):
messages.error(request,'You do not qualify for a trial')
url = '/rowers/paidplans'
return HttpResponseRedirect(url)
@@ -68,7 +182,7 @@ def start_trial_view(request):
def start_plantrial_view(request):
r = getrower(request.user)
- if r.plantrialexpires > datetime.date(1970,1,1):
+ if not can_start_plantrial(request.user):
messages.error(request,'You do not qualify for a trial')
url = '/rowers/paidplans'
return HttpResponseRedirect(url)
@@ -101,6 +215,7 @@ def start_plantrial_view(request):
# Page where user can manage his favorite charts
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def rower_favoritecharts_view(request,userid=0):
message = ''
successmessage = ''
@@ -168,6 +283,7 @@ def rower_favoritecharts_view(request,userid=0):
# page where user sets his export settings
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def rower_exportsettings_view(request,userid=0):
r = getrequestrower(request,userid=userid)
if request.method == 'POST':
@@ -204,6 +320,7 @@ def rower_exportsettings_view(request,userid=0):
# Page where user can set his details
# Add email address to form so user can change his email address
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def rower_edit_view(request,rowerid=0,userid=0,message=""):
r = getrequestrower(request,rowerid=rowerid,userid=userid,notpermanent=True)
@@ -299,6 +416,7 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""):
# Page where user can set his details
# Add email address to form so user can change his email address
@login_required()
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def rower_prefs_view(request,userid=0,message=""):
r = getrequestrower(request,userid=userid,notpermanent=True)
diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py
index f72537f9..5c6cb869 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)
@@ -386,21 +386,18 @@ def workout_video_create_view(request,id=0):
@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)
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_forcecurve_view(request,id=0,workstrokesonly=False):
row = get_workout(id)
promember=0
mayedit=0
- if not request.user.is_anonymous:
- r = getrower(request.user)
- result = request.user.is_authenticated and ispromember(request.user)
- if result:
- promember=1
- if request.user == row.user.user:
- mayedit=1
- if not promember:
- return HttpResponseRedirect("/rowers/about/")
+ r = getrequestrower(request)
+ promember = 1
+
+ if r == row.user:
+ mayedit = 1
if request.method == 'POST':
form = ForceCurveOptionsForm(request.POST)
@@ -457,12 +454,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 +476,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()
@@ -507,21 +500,15 @@ def otw_use_gps(request,id=0):
# Show Stroke power histogram for a workout
@login_required()
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_histo_view(request,id=0):
w = get_workout(id)
+ r = getrequestrower(request)
+ promember = 1
- promember=0
mayedit=0
- if not request.user.is_anonymous:
- r = getrower(request.user)
- result = request.user.is_authenticated and ispromember(request.user)
- if result:
- promember=1
- if request.user == w.user.user:
- mayedit=1
-
- if not promember:
- return HttpResponseRedirect("/rowers/about/")
+ if w.user == r:
+ mayedit = 1
res = interactive_histoall([w],'power',False)
script = res[0]
@@ -748,20 +735,14 @@ 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)
+@permission_required('rower.is_coach',fn=get_user_by_userid,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 +759,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:
@@ -815,8 +789,11 @@ def workout_recalcsummary_view(request,id=0):
@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 workouts_join_view(request):
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
+def workouts_join_view(request,userid=0):
promember=0
+
+ r = getrequestrower(request,userid=userid)
if not request.user.is_anonymous:
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
@@ -869,6 +846,7 @@ defaultoptions = {
@user_passes_test(ispromember, login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
+@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def video_selectworkout(request,userid=0,teamid=0):
r = getrequestrower(request, userid=userid)
user = r.user
@@ -1057,6 +1035,7 @@ def video_selectworkout(request,userid=0,teamid=0):
})
+@permission_required('rower.is_coach',fn=get_user_by_userid,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)
@@ -1065,16 +1044,15 @@ def workouts_join_select(request,
enddatestring="",
message='',
successmessage='',
+ userid=0,
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now()+datetime.timedelta(days=1),
teamid=0):
- try:
- r = getrower(request.user)
- except Rower.DoesNotExist:
- raise Http404("Rower doesn't exist")
+ r = getrequestrower(request,userid=userid)
+
if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype']
else:
@@ -1147,9 +1125,6 @@ def workouts_join_select(request,
except Team.DoesNotExist:
theteam = 0
- if r.rowerplan == 'basic' and theteam==0:
- raise PermissionDenied("Access denied")
-
if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
workouts = Workout.objects.filter(team=theteam,
startdatetime__gte=startdate,
@@ -1232,6 +1207,7 @@ def team_comparison_select(request,
r = getrequestrower(request,userid=userid)
requestrower = getrower(request.user)
+
request.session.pop('ps',None)
if 'waterboattype' in request.session:
@@ -1359,7 +1335,8 @@ def team_comparison_select(request,
if id:
firstworkout = get_workout(id)
- if not checkworkoutuserview(request.user,firstworkout):
+ if not is_workout_team(request.user,firstworkout):
+
raise PermissionDenied("You are not allowed to use this workout")
firstworkoutquery = Workout.objects.filter(id=encoder.decode_hex(id))
@@ -1582,7 +1559,8 @@ def virtualevent_compare_view(request,id=0):
})
-@login_required()
+@permission_required('plannedsession.view_session',
+ fn=get_session_by_pk,raise_exception=True)
def plannedsession_compare_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
@@ -1591,18 +1569,6 @@ def plannedsession_compare_view(request,id=0,userid=0):
except PlannedSession.DoesNotExist:
raise Http404("Planned session does not exist")
- m = ps.manager
- mm = m.rower
-
- if ps.manager != request.user:
- if r.rowerplan == 'coach':
- teams = Team.objects.filter(manager=request.user)
- members = Rower.objects.filter(team__in=teams).distinct()
- teamusers = [m.user for m in members]
- if ps.manager not in teamusers:
- raise PermissionDenied("You do not have access to this session")
- elif r not in ps.rower.all():
- raise PermissionDenied("You do not have access to this session")
workouts = Workout.objects.filter(plannedsession=ps)
@@ -1789,10 +1755,11 @@ def workouts_view(request,message='',successmessage='',
request.session['referer'] = absolute(request)['PATH']
r = getrequestrower(request,rowerid=rowerid,userid=userid)
-
# check if access is allowed
- if not checkviewworkouts(request.user,r):
- raise PermissionDenied("Access denied")
+ if not is_rower_team_member(request.user,r):
+ request.session['rowerid'] = request.user.rower.id
+
+ raise PermissionDenied("Access denied")
@@ -2001,17 +1968,15 @@ def workouts_view(request,message='',successmessage='',
@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_fusion_list(request,id=0,message='',successmessage='',
+@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
+def workout_fusion_list(request,id=0,
startdatestring="",enddatestring="",
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now()):
- try:
- r = getrower(request.user)
- except Rower.DoesNotExist:
- raise Http404("User has no rower instance")
+ r = getrequestrower(request)
- u = User.objects.get(id=r.user.id)
+ u = r.user
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
@@ -2039,6 +2004,8 @@ def workout_fusion_list(request,id=0,message='',successmessage='',
if id:
theid = encoder.decode_hex(id)
+ w = get_workout(id)
+ r = w.user
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
@@ -2068,8 +2035,6 @@ def workout_fusion_list(request,id=0,message='',successmessage='',
workouts = paginator.page(paginator.num_pages)
row = get_workout(id)
- messages.info(request,successmessage)
- messages.error(request,message)
breadcrumbs = [
{
@@ -2104,6 +2069,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 +2078,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 +2164,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 +2174,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 +2197,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,20 +2208,27 @@ 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)
if row == 0:
return HttpResponse("Error: CSV Data File Not Found")
- pace = row.df[' Stroke500mPace (sec/500m)'].values
- velo = 500./pace
+ try:
+ pace = row.df[' Stroke500mPace (sec/500m)'].values
+ velo = 500./pace
+ except KeyError:
+ messages.error(request,'Unable to find the data')
+ if previousurl:
+ url = previousurl
+ else:
+ url = reverse(r.defaultlandingpage,
+ kwargs = {
+ 'id':id,
+ }
+ )
+
+ return HttpResponseRedirect(url)
if not 'originalvelo' in row.df:
row.df['originalvelo'] = velo
@@ -2373,6 +2335,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 +2345,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 +2405,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 +2413,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 +2476,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 +2497,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 +2606,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
@@ -2679,7 +2622,9 @@ def workout_stream_view(request,id=0,message="",successmessage=""):
rowdata = rdata(f1)
if rowdata == 0:
- return HttpResponse("Error: CSV Data File Not Found")
+ messages.info(request,"Error: CSV data file not found")
+ url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(row.id)})
+ return HttpResponseRedirect(url)
if request.method == 'POST':
# process form
@@ -2745,23 +2690,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 +2816,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 +2842,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 +2865,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 +2918,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 = [
{
@@ -3023,8 +2944,10 @@ def workout_data_view(request, id=0):
datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id))
-
- datadf.sort_values(['ftime'],inplace=True)
+ try:
+ datadf.sort_values(['ftime'],inplace=True)
+ except KeyError:
+ pass
columns = datadf.columns.values
@@ -3049,11 +2972,13 @@ def workout_data_view(request, id=0):
tcols = ['ftime','cumdist','fpace','spm','hr','power']
datadf = datadf[cols]
- datadf.loc[:,'hr'] = datadf['hr'].astype('int')
- datadf.loc[:,'power'] = datadf['power'].astype('int')
- datadf.loc[:,'distance'] = datadf['distance'].astype('int')
- datadf.loc[:,'spm'] = 10*datadf['spm'].astype('int')/10.
-
+ try:
+ datadf.loc[:,'hr'] = datadf['hr'].astype('int')
+ datadf.loc[:,'power'] = datadf['power'].astype('int')
+ datadf.loc[:,'distance'] = datadf['distance'].astype('int')
+ datadf.loc[:,'spm'] = 10*datadf['spm'].astype('int')/10.
+ except KeyError:
+ pass
if request.method == 'POST':
form = DataFrameColumnsForm(request.POST)
@@ -3066,9 +2991,12 @@ def workout_data_view(request, id=0):
try:
datadf = datadf[tcols]
except KeyError:
- tcols = list(set(datadf.columns(tolist)+tcols))
- datadf = datadf[tcols]
- datadf = datadf.fillna(value=0)
+ # tcols = list(set(datadf.columns(tolist)+tcols))
+ try:
+ datadf = datadf[tcols]
+ datadf = datadf.fillna(value=0)
+ except KeyError:
+ pass
for col in cols:
try:
@@ -3100,7 +3028,7 @@ def workout_data_view(request, id=0):
# Stats page
-@login_required()
+@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_stats_view(request,id=0,message="",successmessage=""):
r = getrower(request.user)
@@ -3109,7 +3037,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 +3063,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 +3304,12 @@ def workout_workflow_config2_view(request,userid=0):
# Workflow View
@login_required()
+@permission_required('workout.view_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)
@@ -3464,6 +3392,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:
@@ -3482,17 +3411,17 @@ def workout_flexchart3_view(request,*args,**kwargs):
favoritenr = -1
row = get_workout(id)
+ r = getrequestrower(request)
promember=0
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'
@@ -3795,14 +3724,12 @@ def workout_otwpowerplot_view(request,id=0,message="",successmessage=""):
# r = getrower(u)
promember=0
- mayedit=0
- if not request.user.is_anonymous:
- r = getrower(request.user)
- result = request.user.is_authenticated and ispromember(request.user)
- if result:
- promember=1
- if request.user == w.user.user:
- mayedit=1
+ mayedit = 0
+ result = request.user.is_authenticated and ispromember(request.user)
+ if result:
+ promember=1
+ if request.user == w.user.user:
+ mayedit=1
# create interactive plot
res = interactive_otw_advanced_pace_chart(encoder.decode_hex(id),promember=promember)
@@ -3977,7 +3904,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,14 +3913,13 @@ 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)])
try:
if data['wash'].std() != 0:
- url = reverse('paidplans')
+ url = reverse('paidplans_view')
messages.info(
request,
'Some Empower Oarlock data are only available to users with a paid plan'.format(u=url)
@@ -4149,7 +4075,9 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
+
rowdata = rdata(f1)
+
hascoordinates = 1
if rowdata != 0:
try:
@@ -4293,7 +4221,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 +4247,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 +4325,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 +4333,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 +4362,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()
@@ -4868,22 +4794,22 @@ def workout_upload_view(request,
return response
else:
if not is_ajax:
- if r.c2_auto_export and isprorower(r):
+ if r.c2_auto_export and ispromember(r.user):
uploadoptions['upload_to_C2'] = True
- if r.strava_auto_export and isprorower(r):
+ if r.strava_auto_export and ispromember(r.user):
uploadoptions['upload_to_Strava'] = True
- if r.sporttracks_auto_export and isprorower(r):
+ if r.sporttracks_auto_export and ispromember(r.user):
uploadoptions['upload_to_SportTracks'] = True
- if r.runkeeper_auto_export and isprorower(r):
+ if r.runkeeper_auto_export and ispromember(r.user):
uploadoptions['upload_to_RunKeeper'] = True
- if r.trainingpeaks_auto_export and isprorower(r):
+ if r.trainingpeaks_auto_export and ispromember(r.user):
uploadoptions['upload_to_TrainingPeaks'] = True
- if r.mapmyfitness_auto_export and isprorower(r):
+ if r.mapmyfitness_auto_export and ispromember(r.user):
uploadoptions['upload_to_MapMyFitness'] = True
form = DocumentsForm(initial=docformoptions)
@@ -4969,7 +4895,7 @@ def team_workout_upload_view(request,message="",
workouttype = form.cleaned_data['workouttype']
if rowerform.is_valid():
u = rowerform.cleaned_data['user']
- if u and request.user.rower in teams.rower_get_coaches(u.rower):
+ if can_add_workout_member(request.user,u.rower):
r = getrower(u)
else:
message = 'Please select a rower'
@@ -5150,46 +5076,45 @@ def list_videos(request):
})
@login_required()
-def graphs_view(request):
+def graphs_view(request,userid=0):
request.session['referer'] = reverse('graphs_view')
+ r = getrequestrower(request,userid=userid)
+ workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
+ query = request.GET.get('q')
+ if query:
+ query_list = query.split()
+ workouts = workouts.filter(
+ reduce(operator.and_,
+ (Q(name__icontains=q) for q in query_list)) |
+ reduce(operator.and_,
+ (Q(notes__icontains=q) for q in query_list))
+ )
+ searchform = SearchForm(initial={'q':query})
+ else:
+ searchform = SearchForm()
+
+ g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime")
+
+
+ paginator = Paginator(g,8)
+ page = request.GET.get('page')
+
try:
- r = getrower(request.user)
- workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
- query = request.GET.get('q')
- if query:
- query_list = query.split()
- workouts = workouts.filter(
- reduce(operator.and_,
- (Q(name__icontains=q) for q in query_list)) |
- reduce(operator.and_,
- (Q(notes__icontains=q) for q in query_list))
- )
- searchform = SearchForm(initial={'q':query})
- else:
- searchform = SearchForm()
+ g = paginator.page(page)
+ except PageNotAnInteger:
+ g = paginator.page(1)
+ except EmptyPage:
+ g = paginator.page(paginator.num_pages)
- g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime")
-
-
- paginator = Paginator(g,8)
- page = request.GET.get('page')
-
- try:
- g = paginator.page(page)
- except PageNotAnInteger:
- g = paginator.page(1)
- except EmptyPage:
- g = paginator.page(paginator.num_pages)
-
- return render(request, 'list_graphs.html',
+ return render(request, 'list_graphs.html',
{'graphs': g,
'searchform':searchform,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
+ 'rower':r,
})
- except Rower.DoesNotExist:
- raise Http404("User has no rower instance")
+
# Show the chart (png image)
def graph_show_view(request,id):
@@ -5237,9 +5162,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 +5212,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 +5324,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 +5401,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 = [
{
@@ -5955,7 +5881,7 @@ class VideoDelete(DeleteView):
def get_object(self, *args, **kwargs):
obj = super(VideoDelete, self).get_object(*args, **kwargs)
- if not checkaccessuser(self.request.user,obj.workout.user):
+ if not is_coach_user(self.request.user,obj.workout.user):
raise PermissionDenied('You are not allowed to delete this analysis')
return obj
@@ -6008,7 +5934,8 @@ class GraphDelete(DeleteView):
def get_object(self, *args, **kwargs):
obj = super(GraphDelete, self).get_object(*args, **kwargs)
- if not checkaccessuser(self.request.user,obj.workout.user):
+
+ if not is_workout_user(self.request.user,obj.workout):
raise PermissionDenied('You are not allowed to delete this chart')
return obj
@@ -6024,10 +5951,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 +6010,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
diff --git a/rowers/weather.py b/rowers/weather.py
index 2af994f3..cb31817a 100644
--- a/rowers/weather.py
+++ b/rowers/weather.py
@@ -4,6 +4,7 @@ from __future__ import print_function
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
import requests
+from requests.exceptions import ConnectionError
import json
from lxml import objectify,etree
import xml.etree.ElementTree as ET
@@ -26,13 +27,16 @@ def get_airport_code(lat,lon):
newlon = a.iloc[0]['lon']
distance = geo_distance(lat,lon,newlat,newlon)
return airport_code,newlat,newlon,distance
-
+
# Get weather data from the DarkSky API
def get_weather_data(long,lat,unixtime):
url = "https://api.darksky.net/forecast/"+FORECAST_IO_KEY+"/"
url += str(long)+","+str(lat)+","+str(unixtime)
- s = requests.get(url)
+ try:
+ s = requests.get(url)
+ except ConnectionError:
+ return 0
if s.ok:
return s.json()
@@ -55,10 +59,14 @@ def get_metar_data(airportcode,unixtime):
except:
message = 'Failed to download METAR data'
return [0,0,message,'','']
-
+
if s.ok:
- doc = etree.fromstring(s.content)
+ try:
+ doc = etree.fromstring(s.content)
+ except AttributeError:
+ message = 'METAR data content error'
+ return [0,0,message,'',0]
lengte = len(doc.xpath('data/METAR/station_id'))
idnr = int(lengte/2)
try:
@@ -77,7 +85,7 @@ def get_metar_data(airportcode,unixtime):
wind_knots = float(wind_speed)
wind_ms = 0.514444*wind_knots
wind_ms = int(10*wind_ms)/10.
-
+
temperaturec = float(temp_c)
temp_f = 32.+(9./5.)*temperaturec
temp_f = str(int(10*temp_f)/10.)
@@ -87,17 +95,19 @@ def get_metar_data(airportcode,unixtime):
message += '. Wind: '+str(wind_ms)+' m/s ('+str(wind_knots)+' kt)'
message +='. Wind Bearing: '+str(windbearing)+' degrees'
# message +='\n'+rawtext
-
+
return [wind_ms,windbearing,message,rawtext,timestamp]
message = 'Failed to download METAR data'
return [0,0,message,'',timestamp]
-
+
# Get wind data (and translate from knots to m/s)
def get_wind_data(lat,long,unixtime):
data = get_weather_data(lat,long,unixtime)
+ summary = ''
+ temperature = 20
if data:
try:
# we are getting wind in mph
@@ -107,11 +117,13 @@ def get_wind_data(lat,long,unixtime):
windspeed = 0
windbearing = 0
+
try:
airports = data['flags']['madis-stations']
except KeyError:
airports = ['unknown']
+
try:
temperature = data['currently']['temperature']
# Temp is given in Fahrenheit, so convert to Celsius for Europeans
@@ -121,6 +133,7 @@ def get_wind_data(lat,long,unixtime):
temperature = 'unknown'
temperaturec = 'unknown'
+
try:
summary = data['currently']['summary']
except KeyError:
@@ -128,15 +141,19 @@ def get_wind_data(lat,long,unixtime):
else:
windspeed = 0
windbearing = 0
+ summary = 'unknown'
+ airports = ['unknown']
+ temparature = 'unknown'
+ temperaturec = 'unknown'
message = 'Not able to get weather data'
# apply Hellman's coefficient for neutral air above human
# inhabitated areas
windspeed = windspeed*(0.1)**0.34
windspeed = 0.01*int(100*windspeed)
-
+
timestamp = arrow.get(unixtime).isoformat()
-
+
message = 'Summary for your location at '+timestamp+': '+summary
message += '. Temperature '+str(temperature)+'F/'+str(temperaturec)+'C'
diff --git a/rowsandall_app/settings.py b/rowsandall_app/settings.py
index 4ab15697..c453669c 100644
--- a/rowsandall_app/settings.py
+++ b/rowsandall_app/settings.py
@@ -73,11 +73,13 @@ INSTALLED_APPS = [
'tz_detect',
'django_social_share',
'django_countries',
+ 'rules',
]
AUTHENTICATION_BACKENDS = (
'oauth2_provider.backends.OAuth2Backend',
# Uncomment following if you want to access the admin
+ 'rules.permissions.ObjectPermissionBackend',
'django.contrib.auth.backends.ModelBackend',
)
diff --git a/rowsandall_app/urls.py b/rowsandall_app/urls.py
index b6bcca18..21c5e454 100644
--- a/rowsandall_app/urls.py
+++ b/rowsandall_app/urls.py
@@ -66,7 +66,7 @@ urlpatterns += [
{'next_page': '/'},
name='logout',),
re_path(r'^rowers/',include('rowers.urls')),
- re_path(r'^survey/',include('survey.urls')),
+# re_path(r'^survey/',include('survey.urls')),
# re_path(r'^cvkbrno/',include('cvkbrno.urls')),
# re_path(r'^admin/rq/',include('django_rq_dashboard.urls')),
re_path(r'^call\_back',rowersviews.rower_process_callback),
diff --git a/templates/newbase.html b/templates/newbase.html
index 5fab2852..02d3d0d4 100644
--- a/templates/newbase.html
+++ b/templates/newbase.html
@@ -246,7 +246,7 @@
{% endif %}
{% endif %}
{% if user.rower.protrialexpires and user.rower.protrialexpires|is_future_date %}
- {% if user.rower.plantrialexpires and user.rower.rowerplan != 'plan' %}
+ {% if user.rower.plantrialexpires and user.rower.plantrialexpires|is_future_date and user.rower.rowerplan != 'plan' %}
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial - Would you like to upgrade now?