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 @@
-
-
+ Delete +
+ + + + ++ Click on session name to clone to current period +
+| Name | +Value | ++ | Edit | +Delete | +
|---|---|---|---|---|
| + {% if ps.name != '' %} + {{ ps.name }} + {% else %} + Unnamed Session + {% endif %} + | +{{ ps.sessionvalue }} | +{{ ps.sessionunit }} | ++ Edit Template + | ++ Delete + | +
Edit Session + / + Save to template
{% endif %}\w+.*)/accept/$',views.rower_accept_coachoffer_view,
name='rower_accept_coachoffer_view'),
- re_path(r'^team/(?P\d+)/delete/$',views.team_delete_view,name='team_delete_view'),
+ re_path(r'^team/(?P\d+)/delete/$',views.team_delete_view,name='team_delete_view'),
re_path(r'^team/create/$',views.team_create_view,name='team_create_view'),
re_path(r'^me/team/(?P\d+)/drop/(?P\d+)/$',views.manager_member_drop_view,name='manager_member_drop_view'),
re_path(r'^me/invitation/(?P\d+)/reject/$',views.invitation_reject_view,name='invitation_reject_view'),
@@ -535,10 +539,10 @@ urlpatterns = [
# re_path(r'^compatibility', TemplateView.as_view(template_name='compatibility.html'),name='about'),
# re_path(r'^videos', TemplateView.as_view(template_name='videos.html'),name='videos'),
re_path(r'^analysis/user/(?P\d+)/$', views.analysis_view,name='analysis'),
- re_path(r'^laboratory/user/(?P\d+)/$', views.laboratory_view,name='laboratory'),
+ re_path(r'^laboratory/$', views.laboratory_view,name='laboratory_view'),
+ re_path(r'^laboratory/user/(?P\d+)/$', views.laboratory_view,name='laboratory_view'),
re_path(r'^errormessage/(?P[\w\ ]+.*)/$',views.errormessage_view,name='errormessage_view'),
re_path(r'^analysis/$', views.analysis_view,name='analysis'),
- re_path(r'^laboratory/$', views.laboratory_view,name='laboratory'),
re_path(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'),
re_path(r'^checkout/(?P\d+)/$',views.payment_confirm_view,name='payment_confirm_view'),
re_path(r'^upgradecheckout/(?P\d+)/$',views.upgrade_confirm_view,name='upgrade_confirm_view'),
@@ -549,7 +553,7 @@ urlpatterns = [
re_path(r'^downgrade/$',views.downgrade_view,name='downgrade'),
re_path(r'^paymentcompleted/$',views.payment_completed_view,name='payment_completed_view'),
re_path(r'^downgradecompleted/$',views.downgrade_completed_view,name='downgrade_completed_view'),
- re_path(r'^paidplans/$',views.paidplans_view,name='paidplans'),
+ re_path(r'^paidplans/$',views.paidplans_view,name='paidplans_view'),
re_path(r'^me/cancelsubscriptions/$',views.plan_stop_view,name='plan_stop_view'),
re_path(r'^me/cancelsubscription/(?P[\w\ ]+.*)/$',views.plan_tobasic_view,name='plan_tobasic_view'),
re_path(r'^checkouts/$',views.checkouts_view,name='checkouts'),
@@ -574,7 +578,7 @@ urlpatterns = [
# re_path(r'^workout/compare/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P[\w\ ]+.*)/$',views.workout_comparison_view2),
re_path(r'^test\_callback',views.rower_process_testcallback,name='rower_process_testcallback'),
re_path(r'^createplan/$',views.rower_create_trainingplan,name='rower_create_trainingplan'),
- re_path(r'^createplan/user/(?P\d+)/$',views.rower_create_trainingplan,name='rower_create_trainingplan'),
+ re_path(r'^createplan/user/(?P\d+)/$',views.rower_create_trainingplan,name='rower_create_trainingplan'),
re_path(r'^deleteplan/(?P\d+)/$',login_required(
views.TrainingPlanDelete.as_view()),name='trainingplan_delete_view'),
re_path(r'^deletemicrocycle/(?P\d+)/$',login_required(
@@ -637,9 +641,9 @@ urlpatterns = [
re_path(r'^sessions/teamcreate/team/$',
views.plannedsession_teamcreate_view,
name='plannedsession_teamcreate_view'),
- re_path(r'^sessions/teamedit/(?P\d+)/$',views.plannedsession_teamedit_view,
+ re_path(r'^sessions/teamedit/(?P\d+)/$',views.plannedsession_teamedit_view,
name='plannedsession_teamedit_view'),
- re_path(r'^sessions/teamedit/(?P\d+)/user/(?P\d+)/$',
+ re_path(r'^sessions/teamedit/(?P\d+)/user/(?P\d+)/$',
views.plannedsession_teamedit_view,
name='plannedsession_teamedit_view'),
re_path(r'^sessions/create/$',views.plannedsession_create_view,
@@ -653,7 +657,7 @@ urlpatterns = [
# re_path(r'^sessions/create/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/user/(?P\d+)/$',
# views.plannedsession_create_view,
# name='plannedsession_create_view'),
- re_path(r'^sessions/multiclone/$',views.plannedsession_multiclone_view),
+ re_path(r'^sessions/multiclone/$',views.plannedsession_multiclone_view,name='plannedsession_multiclone_view'),
re_path(r'^sessions/multiclone/user/(?P\d+)/$',
views.plannedsession_multiclone_view,
name='plannedsession_multiclone_view'),
@@ -666,6 +670,7 @@ urlpatterns = [
views.plannedsession_multicreate_view,
name='plannedsession_multicreate_view'),
re_path(r'^sessions/(?P\d+)/edit/$',views.plannedsession_edit_view),
+ re_path(r'^sessions/(?P\d+)/templateedit/',views.plannedsession_templateedit_view),
re_path(r'^sessions/(?P\d+)/maketemplate/$',views.plannedsession_totemplate_view),
re_path(r'^sessions/(?P\d+)/compare/$',
views.plannedsession_compare_view,
@@ -716,7 +721,7 @@ urlpatterns = [
re_path(r'^sessions/print/user/(?P\d+)/$',views.plannedsessions_print_view,
name='plannedsessions_print_view'),
re_path(r'^sessions/sendcalendar/$',views.plannedsessions_icsemail_view,
- name='plannedsessions_icsemail_view'),
+ name='plannedsessions_coach_icsemail_view'),
re_path(r'^sessions/sendcalendar/user/(?P\d+)/$',views.plannedsessions_icsemail_view,
name='plannedsessions_icsemail_view'),
re_path(r'^sessions/$',views.plannedsessions_view,
diff --git a/rowers/utils.py b/rowers/utils.py
index d4e47993..a1258b1c 100644
--- a/rowers/utils.py
+++ b/rowers/utils.py
@@ -109,7 +109,7 @@ def get_call():
""" % (call1, call2)
-
+
return call
def absolute(request):
@@ -131,7 +131,7 @@ def trcolors(r1,g1,b1,r2,g2,b2):
h1,s1,v1 = colorsys.rgb_to_hsv(r1,g1,b1)
h2,s2,v2 = colorsys.rgb_to_hsv(r2,g2,b2)
-
+
return 360*h1,360*(h2-h1),s1,(s2-s1),v1,(v2-v1)
palettes = {
@@ -167,7 +167,7 @@ def is_ranking_piece(workout):
return True
elif workout.duration in rankingdurations:
return True
-
+
return False
def range_to_color_hex(groupcols,palette='monochrome_blue'):
@@ -176,20 +176,20 @@ def range_to_color_hex(groupcols,palette='monochrome_blue'):
plt = palettes[palette]
except KeyError:
plt = palettes['monochrome_blue']
-
+
rgb = [colorsys.hsv_to_rgb((plt[0]+plt[1]*x)/360.,
plt[2]+plt[3]*x,
plt[4]+plt[5]*x) for x in groupcols]
RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb]
colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB]
-
+
return colors
def str2bool(v):
return v.lower() in ("yes", "true", "t", "1")
-def uniqify(seq, idfun=None):
+def uniqify(seq, idfun=None):
# order preserving
if idfun is None:
def idfun(x): return x
@@ -222,12 +222,12 @@ def geo_distance(lat1,lon1,lat2,lon2):
This is a slight underestimate but is close enough for our purposes,
We're never moving more than 10 meters between trackpoints
- Bearing calculation fails if one of the points is a pole.
- (Hey, from the North pole you can walk South, East, North and end up
+ Bearing calculation fails if one of the points is a pole.
+ (Hey, from the North pole you can walk South, East, North and end up
on the same spot!)
-
+
"""
-
+
# radius of our earth in km --> should be moved to settings if
# rowing takes off on other planets
R = 6373.0
@@ -257,7 +257,7 @@ def geo_distance(lat1,lon1,lat2,lon2):
return [distance,bearing]
-
+
def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
pwr = abs(p0)/(1+(delta/abs(p2)))
@@ -268,7 +268,7 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
pwr *= ratio
-
+
delta = delta.values.astype(int)
cpvalues = cpvalues.values.astype(int)
pwr = pwr.astype(int)
@@ -276,7 +276,7 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
res = np.sum(cpvalues>pwr)
res2 = np.sum(cpvalues>pwr2)
-
+
btdf = pd.DataFrame(
{
'delta':delta[cpvalues>pwr],
@@ -288,7 +288,7 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
btdf.sort_values('delta',axis=0,inplace=True)
-
+
return res>1,btdf,res2>1
@@ -300,7 +300,7 @@ def myqueue(queue,function,*args,**kwargs):
def revoke(self):
return 1
-
+
if settings.TESTING:
return MockJob()
elif settings.CELERY:
@@ -310,7 +310,7 @@ def myqueue(queue,function,*args,**kwargs):
else:
if settings.DEBUG:
kwargs['debug'] = True
-
+
job_id = str(uuid.uuid4())
kwargs['job_id'] = job_id
kwargs['jobkey'] = job_id
@@ -335,7 +335,7 @@ def my_dict_from_instance(instance,model):
thedict['id'] = instance.id
for f in instance._meta.fields:
-
+
fname = f.name
try:
@@ -405,24 +405,13 @@ def totaltime_sec_to_string(totaltime):
return duration
-def isprorower(r):
- result = False
- result = r.rowerplan in ['pro','coach','plan']
-
- if not result and r.protrialexpires:
- result = r.rowerplan == 'basic' and r.protrialexpires >= datetime.date.today()
- if not result and r.rowerplan == 'freecoach':
- if r.mycoachgroup is not None:
- result = len(r.mycoachgroup)>=4
-
- return result
def iscoach(m,r):
result = False
result = m in r.coaches
return result
-
+
# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
@@ -433,7 +422,7 @@ def ewmovingaverage(interval,window_size):
intervaldf = pd.DataFrame({'v':interval})
idf_ewma1 = intervaldf.ewm(span=window_size)
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
-
+
i_ewma1 = idf_ewma1.mean().loc[:,'v']
i_ewma2 = idf_ewma2.mean().loc[:,'v']
@@ -478,7 +467,7 @@ def custom_exception_handler(exc,message):
res.json = json.dumps(response)
return res
-
+
def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high'):
authorizationstring = str('Bearer ' + r.stravatoken)
headers = {'Authorization': authorizationstring,
@@ -503,7 +492,7 @@ def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high
return np.array(data['data'])
except TypeError:
return None
-
+
return None
def allmonths(startdate,enddate):
@@ -518,4 +507,3 @@ def allsundays(startdate,enddate):
while d<=enddate:
yield d
d += datetime.timedelta(days=7)
-
diff --git a/rowers/views/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 05910fbc..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,
@@ -704,7 +685,9 @@ def plannedsession_teamcreate_view(request,
enddatestring = enddate.strftime('%Y-%m-%d')
url += '?when='+startdatestring+'/'+enddatestring
- return HttpResponseRedirect(url)
+ next = request.GET.get('next', url)
+
+ return HttpResponseRedirect(next)
else:
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
breadcrumbs = [
@@ -786,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]
@@ -890,15 +868,15 @@ def plannedsession_teamedit_view(request,
url = reverse(plannedsession_teamedit_view,
kwargs = {
- 'sessionid':sessionid,
+ 'id':id,
})
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
url += '?when='+startdatestring+'/'+enddatestring
-
- return HttpResponseRedirect(url)
+ next = request.GET.get('next', url)
+ return HttpResponseRedirect(next)
else:
sessioncreateform = PlannedSessionForm(instance=ps)
sessionteamselectform = PlannedSessionTeamForm(
@@ -1238,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):
@@ -1245,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)
@@ -1402,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):
@@ -1420,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()
@@ -1465,12 +1437,13 @@ def plannedsession_clone_view(request,id=0,userid=0):
enddatestring = enddate.strftime('%Y-%m-%d')
url += '?when='+startdatestring+'/'+enddatestring
-
- return HttpResponseRedirect(url)
+ next = request.GET.get('next', url)
+ return HttpResponseRedirect(next)
# 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):
@@ -1497,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
@@ -1522,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,
}
)
@@ -1531,54 +1498,16 @@ def plannedsession_teamclone_view(request,id=0):
enddatestring = enddate.strftime('%Y-%m-%d')
url += '?when='+startdatestring+'/'+enddatestring
+ next = request.GET.get('next', url)
+ return HttpResponseRedirect(next)
- return HttpResponseRedirect(url)
-
-@user_passes_test(hasplannedsessions, 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):
-
- r = getrequestplanrower(request)
-
- 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.pk = None
- ps.id = None
- ps.is_template = True
- ps.startdate = datetime.date(1970,1,1)
- ps.enddate = datetime.date(1970,1,1)
- ps.save()
-
- url = reverse(plannedsession_create_view,kwargs={'userid':r.user.id})
-
- startdatestring = startdate.strftime('%Y-%m-%d')
- enddatestring = enddate.strftime('%Y-%m-%d')
- url += '?when='+startdatestring+'/'+enddatestring
-
- return HttpResponseRedirect(url)
-
-# Edit an existing planned session
-@user_passes_test(hasplannedsessions,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):
-
- r = getrequestplanrower(request,userid=userid)
-
-
-
+@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):
+ r = getrequestrower(request)
startdate,enddate = get_dates_timeperiod(request)
-
-
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
@@ -1598,10 +1527,131 @@ def plannedsession_edit_view(request,id=0,userid=0):
if ps.sessiontype in ['race','indoorrace']:
raise PermissionDenied("You are not allowed to edit this planned session because it is a race")
+ if not ps.is_template:
+ ps.pk = None
+ ps.id = None
+ ps.is_template = True
+ ps.startdate = datetime.date(1970,1,1)
+ ps.enddate = datetime.date(1970,1,1)
+ ps.team.clear()
+ ps.save()
+
+ sessioncreateform = PlannedSessionTemplateForm(instance=ps)
+
+ if request.method == 'POST':
+ sessioncreateform = PlannedSessionTemplateForm(request.POST,instance=ps)
+ if sessioncreateform.is_valid():
+ cd = sessioncreateform.cleaned_data
+ if cd['sessionunit'] == 'min':
+ cd['sessionmode'] = 'time'
+ elif cd['sessionunit'] in ['km','m']:
+ cd['sessionmode'] = 'distance'
+
+ res, message = update_plannedsession(ps,cd)
+
+ if res:
+ messages.info(request,message)
+ else:
+ messages.error(request,message)
+
+ url = reverse(plannedsession_templateedit_view,
+ kwargs = {
+ 'id':int(ps.id),
+ })
+
+ startdatestring = startdate.strftime('%Y-%m-%d')
+ enddatestring = enddate.strftime('%Y-%m-%d')
+ url += '?when='+startdatestring+'/'+enddatestring
+ next = request.GET.get('next', url)
+ return HttpResponseRedirect(next)
+
+ breadcrumbs = [
+ {
+ 'url': reverse(plannedsessions_view),
+ 'name': 'Sessions'
+ },
+ {
+ 'url':reverse(plannedsession_templateedit_view,
+ kwargs={
+ 'id':id,
+ }
+ ),
+ 'name': 'Edit'
+ }
+ ]
+
+ sessiontemplates = PlannedSession.objects.filter(manager=request.user,is_template=True)
+
+ return render(request,'plannedsessiontemplateedit.html',
+ {
+ 'teams':get_my_teams(request.user),
+ 'plan': trainingplan,
+ 'breadcrumbs': breadcrumbs,
+ 'form': sessioncreateform,
+ 'active':'nav-plan',
+ 'thesession': ps,
+ 'sessiontemplates': sessiontemplates,
+ 'rower': r,
+ })
+
+
+@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):
+
+ r = getrequestplanrower(request)
+
+ startdate, enddate = get_dates_timeperiod(request)
+
+ ps = get_object_or_404(PlannedSession,pk=id)
+
+ ps.pk = None
+ ps.id = None
+ ps.is_template = True
+ ps.startdate = datetime.date(1970,1,1)
+ ps.enddate = datetime.date(1970,1,1)
+ ps.save()
+
+ url = reverse(plannedsession_create_view,kwargs={'userid':r.user.id})
+
+ startdatestring = startdate.strftime('%Y-%m-%d')
+ enddatestring = enddate.strftime('%Y-%m-%d')
+ url += '?when='+startdatestring+'/'+enddatestring
+
+ next = request.GET.get('next', url)
+
+ return HttpResponseRedirect(next)
+
+# Edit an existing planned session
+@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):
+
+ r = getrequestplanrower(request,userid=userid)
+
+
+
+ startdate,enddate = get_dates_timeperiod(request)
+
+
+ try:
+ trainingplan = TrainingPlan.objects.filter(
+ startdate__lte = startdate,
+ rowers = r,
+ enddate__gte = enddate)[0]
+ except IndexError:
+ trainingplan = None
+
+ 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)
@@ -1697,38 +1747,29 @@ 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})
-
- return HttpResponseRedirect(url)
+ next = request.GET.get('next', url)
+ 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',
@@ -1745,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()
@@ -1958,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)
@@ -2041,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(
@@ -2076,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'
}
]
@@ -2099,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)
@@ -2145,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
@@ -2211,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
@@ -2273,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
@@ -2327,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,
@@ -2350,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")
@@ -2423,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,
@@ -2436,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,
@@ -2570,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'
@@ -2644,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:
@@ -2722,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')
@@ -2787,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
@@ -2839,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:
@@ -2901,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 aab50741..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 (
@@ -107,7 +120,7 @@ from rowers.models import (
WorkoutComment,WorkoutCommentForm,RowerExportForm,
CalcAgePerformance,
PowerTimeFitnessMetric,BlogPost,
- PlannedSessionForm,
+ PlannedSessionForm,PlannedSessionTemplateForm,
PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace,
VirtualRaceForm,VirtualRaceResultForm,RowerImportExportForm,
IndoorVirtualRaceResultForm,IndoorVirtualRaceResult,
@@ -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' %}