From 7f6112462fc0d9a888e4b93f44ffe35003c6cb6e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 23 Dec 2019 21:08:05 +0100 Subject: [PATCH 01/80] helper files --- rowers/rowing_workout_metrics_pb2.py | 203 ++++++++++++++++++++++ rowers/rowing_workout_metrics_pb2_grpc.py | 46 +++++ 2 files changed, 249 insertions(+) create mode 100644 rowers/rowing_workout_metrics_pb2.py create mode 100644 rowers/rowing_workout_metrics_pb2_grpc.py diff --git a/rowers/rowing_workout_metrics_pb2.py b/rowers/rowing_workout_metrics_pb2.py new file mode 100644 index 00000000..53d92d53 --- /dev/null +++ b/rowers/rowing_workout_metrics_pb2.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: rowing-workout-metrics.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='rowing-workout-metrics.proto', + package='rowing_workout_metrics', + syntax='proto3', + serialized_options=None, + serialized_pb=_b('\n\x1crowing-workout-metrics.proto\x12\x16rowing_workout_metrics\"p\n\x15WorkoutMetricsRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x0b\n\x03sex\x18\x02 \x01(\t\x12\x0b\n\x03\x66tp\x18\x03 \x01(\x01\x12\r\n\x05hrftp\x18\x04 \x01(\x01\x12\r\n\x05hrmax\x18\x05 \x01(\x01\x12\r\n\x05hrmin\x18\x06 \x01(\x01\"p\n\x16WorkoutMetricsResponse\x12\x0b\n\x03tss\x18\x01 \x01(\x01\x12\r\n\x05normp\x18\x02 \x01(\x01\x12\r\n\x05trimp\x18\x03 \x01(\x01\x12\r\n\x05hrtss\x18\x04 \x01(\x01\x12\r\n\x05normv\x18\x05 \x01(\x01\x12\r\n\x05normw\x18\x06 \x01(\x01\x32w\n\x07Metrics\x12l\n\x0b\x43\x61lcMetrics\x12-.rowing_workout_metrics.WorkoutMetricsRequest\x1a..rowing_workout_metrics.WorkoutMetricsResponseb\x06proto3') +) + + + + +_WORKOUTMETRICSREQUEST = _descriptor.Descriptor( + name='WorkoutMetricsRequest', + full_name='rowing_workout_metrics.WorkoutMetricsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='filename', full_name='rowing_workout_metrics.WorkoutMetricsRequest.filename', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='sex', full_name='rowing_workout_metrics.WorkoutMetricsRequest.sex', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='ftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.ftp', index=2, + number=3, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='hrftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrftp', index=3, + number=4, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='hrmax', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmax', index=4, + number=5, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='hrmin', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmin', index=5, + number=6, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=56, + serialized_end=168, +) + + +_WORKOUTMETRICSRESPONSE = _descriptor.Descriptor( + name='WorkoutMetricsResponse', + full_name='rowing_workout_metrics.WorkoutMetricsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='tss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.tss', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='normp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normp', index=1, + number=2, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='trimp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.trimp', index=2, + number=3, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='hrtss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.hrtss', index=3, + number=4, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='normv', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normv', index=4, + number=5, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='normw', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normw', index=5, + number=6, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=170, + serialized_end=282, +) + +DESCRIPTOR.message_types_by_name['WorkoutMetricsRequest'] = _WORKOUTMETRICSREQUEST +DESCRIPTOR.message_types_by_name['WorkoutMetricsResponse'] = _WORKOUTMETRICSRESPONSE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +WorkoutMetricsRequest = _reflection.GeneratedProtocolMessageType('WorkoutMetricsRequest', (_message.Message,), { + 'DESCRIPTOR' : _WORKOUTMETRICSREQUEST, + '__module__' : 'rowing_workout_metrics_pb2' + # @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsRequest) + }) +_sym_db.RegisterMessage(WorkoutMetricsRequest) + +WorkoutMetricsResponse = _reflection.GeneratedProtocolMessageType('WorkoutMetricsResponse', (_message.Message,), { + 'DESCRIPTOR' : _WORKOUTMETRICSRESPONSE, + '__module__' : 'rowing_workout_metrics_pb2' + # @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsResponse) + }) +_sym_db.RegisterMessage(WorkoutMetricsResponse) + + + +_METRICS = _descriptor.ServiceDescriptor( + name='Metrics', + full_name='rowing_workout_metrics.Metrics', + file=DESCRIPTOR, + index=0, + serialized_options=None, + serialized_start=284, + serialized_end=403, + methods=[ + _descriptor.MethodDescriptor( + name='CalcMetrics', + full_name='rowing_workout_metrics.Metrics.CalcMetrics', + index=0, + containing_service=None, + input_type=_WORKOUTMETRICSREQUEST, + output_type=_WORKOUTMETRICSRESPONSE, + serialized_options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_METRICS) + +DESCRIPTOR.services_by_name['Metrics'] = _METRICS + +# @@protoc_insertion_point(module_scope) diff --git a/rowers/rowing_workout_metrics_pb2_grpc.py b/rowers/rowing_workout_metrics_pb2_grpc.py new file mode 100644 index 00000000..099bf538 --- /dev/null +++ b/rowers/rowing_workout_metrics_pb2_grpc.py @@ -0,0 +1,46 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import rowing_workout_metrics_pb2 as rowing__workout__metrics__pb2 + + +class MetricsStub(object): + """OTW-metrics service definition + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.CalcMetrics = channel.unary_unary( + '/rowing_workout_metrics.Metrics/CalcMetrics', + request_serializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.SerializeToString, + response_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.FromString, + ) + + +class MetricsServicer(object): + """OTW-metrics service definition + """ + + def CalcMetrics(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_MetricsServicer_to_server(servicer, server): + rpc_method_handlers = { + 'CalcMetrics': grpc.unary_unary_rpc_method_handler( + servicer.CalcMetrics, + request_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.FromString, + response_serializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'rowing_workout_metrics.Metrics', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) From 2388edcf6d43308ad0cc2e93d313498d78a7cafe Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 23 Dec 2019 21:25:00 +0100 Subject: [PATCH 02/80] async calctrimp task using microservice --- rowers/rowing_workout_metrics_pb2_grpc.py | 2 +- rowers/tasks.py | 128 ++++++---------------- 2 files changed, 34 insertions(+), 96 deletions(-) diff --git a/rowers/rowing_workout_metrics_pb2_grpc.py b/rowers/rowing_workout_metrics_pb2_grpc.py index 099bf538..b58e9986 100644 --- a/rowers/rowing_workout_metrics_pb2_grpc.py +++ b/rowers/rowing_workout_metrics_pb2_grpc.py @@ -1,7 +1,7 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! import grpc -import rowing_workout_metrics_pb2 as rowing__workout__metrics__pb2 +import rowers.rowing_workout_metrics_pb2 as rowing__workout__metrics__pb2 class MetricsStub(object): diff --git a/rowers/tasks.py b/rowers/tasks.py index 8c455a4e..727c3b41 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -38,6 +38,8 @@ from matplotlib import path import grpc import rowers.otw_power_calculator_pb2 as calculator_pb2 import rowers.otw_power_calculator_pb2_grpc as calculator_pb2_grpc +import rowers.rowing_workout_metrics_pb2 as metrics_pb2 +import rowers.rowing_workout_metrics_pb2_grpc as metrics_pb2_grpc from rowsandall_app.settings import SITE_URL from rowsandall_app.settings_dev import SITE_URL as SITE_URL_DEV @@ -609,105 +611,41 @@ def handle_calctrimp(id, else: engine = create_engine(database_url, echo=False) - try: - rowdata = rdata(csvfile=csvfilename) - except IOError: + tss = 0 + normp = 0 + trimp = 0 + hrtss = 0 + normv = 0 + normw = 0 + + with grpc.insecure_channel( + target='localhost:50052', + options=[('grpc.lb_policy_name', 'pick_first'), + ('grpc.enable_retries', 0), ('grpc.keepalive_timeout_ms', + 10000)] + ) as channel: try: - rowdata = rdata(csvfile=csvfilename + '.csv') - except IOError: - try: - rowdata = rdata(csvfile=csvfilename + '.gz') - except IOError: - return 0 + grpc.channel_ready_future(channel).result(timeout=10) + except grpc.FutureTimeoutError: + return 0 - df = rowdata.df - - try: - df['deltat'] = df[' ElapsedTime (sec)'].diff().abs() - except KeyError: - return 0 - - df2 = df.copy() - - df2['time'] = df2[' ElapsedTime (sec)'] - df2['time'] = df2['time'].apply( - lambda x:safetimedelta(x) + stub = metrics_pb2_grpc.MetricsStub(channel) + req = metrics_pb2.WorkoutMetricsRequest( + filename = csvfilename, + ftp=ftp, + sex=sex, + hrftp=hrftp, + hrmax=hrmax, + hrmin=hrmin, ) + response = stub.CalcMetrics(req,timeout=60) - duration = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min() - df2 = df2.resample('1s',on='time').mean() - df2.fillna(method='ffill',inplace=True) - df2 = df2.rolling(30).mean() + tss = response.tss + normp = response.normp + trimp = response.trimp + normv = response.normv + normw = response.normw - df2[' Power (watts)'] = df2[' Power (watts)'].abs() - - df2['pwr4'] = df2[' Power (watts)']**(4.0) - # pwr4mean = wavg(df,'pwr4','deltat') - pwr4mean = df2['pwr4'].mean() - pwrmean = df2[' Power (watts)'].mean() - # pwrmean = wavg(df,' Power (watts)','deltat') - if pwr4mean > 0: - normp = (pwr4mean)**(0.25) - else: - normp = pwrmean - - intensityfactor = normp/float(ftp) - tss = 100.*((duration*normp*intensityfactor)/(3600.*ftp)) - - - if sex == 'male': - f = 1.92 - else: - f = 1.67 - - dt = df['TimeStamp (sec)'].diff()/60. - - hrr = (df[' HRCur (bpm)']-hrmin)/(hrmax-hrmin) - hrrftp = (hrftp-hrmin)/float(hrmax-hrmin) - trimp1hr = 60.*hrrftp*0.64*np.exp(f*hrrftp) - - trimpdata = dt*hrr*0.64*np.exp(f*hrr) - trimp = trimpdata.sum() - - hrtss = 100.*trimp/trimp1hr - - pp = 8.0 - - df['v4'] = df[' AverageBoatSpeed (m/s)']**(pp) - v4mean = wavg(df,'v4','deltat') - normv = v4mean**(1./pp) - - try: - df['w4'] = df['driveenergy']**(pp) - w4mean = wavg(df,'w4','deltat') - normw = w4mean**(1./pp) - except KeyError: - normw = 0 - - - if not np.isfinite(normv): - normv = 500./120. - - if not np.isfinite(normw): - normw = 0 - - try: - dum = int(tss) - except ValueError: - tss = 0 - - try: - dum = int(normp) - except ValueError: - normp = 0 - try: - dump = int(trimp) - except ValueError: - trimp = 0 - try: - dump = int(hrtss) - except ValueError: - hrtss = 0 query = 'UPDATE rowers_workout SET rscore = {tss}, normp = {normp}, trimp={trimp}, hrtss={hrtss}, normv={normv}, normw={normw} WHERE id={id}'.format( tss = int(tss), @@ -1603,7 +1541,7 @@ def handle_otwsetpower(self,f1, boattype, weightvalue, try: rowdata = rdata(csvfile) except IOError: - rowdata = rdata(csvfile) + rowdata = rdata(csvfile) else: boatfile = { From 9b653d4d1be8d90baf9d59b527268a420bf9f5c1 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 23 Dec 2019 21:30:26 +0100 Subject: [PATCH 03/80] tasks updated --- rowers/tasks.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rowers/tasks.py b/rowers/tasks.py index 727c3b41..259a7acd 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -618,6 +618,16 @@ def handle_calctrimp(id, normv = 0 normw = 0 + # check what the real file name is + if os.path.exists(csvfilename): + csvfile = csvfilename + elif os.path.exists(csvfilename+'.csv'): + csvfile = csvfilename+'.csv' + elif os.path.exists(csvfilename+'.gz'): + csvfile = csvfilename+'.gz' + + csvfile = os.path.abspath(csvfile) + with grpc.insecure_channel( target='localhost:50052', options=[('grpc.lb_policy_name', 'pick_first'), @@ -631,7 +641,7 @@ def handle_calctrimp(id, stub = metrics_pb2_grpc.MetricsStub(channel) req = metrics_pb2.WorkoutMetricsRequest( - filename = csvfilename, + filename = csvfile, ftp=ftp, sex=sex, hrftp=hrftp, @@ -645,6 +655,7 @@ def handle_calctrimp(id, trimp = response.trimp normv = response.normv normw = response.normw + hrtss = response.hrtss query = 'UPDATE rowers_workout SET rscore = {tss}, normp = {normp}, trimp={trimp}, hrtss={hrtss}, normv={normv}, normw={normw} WHERE id={id}'.format( From 79afd29b190b8110b86e601b6e51a8be15758917 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 10 Jan 2020 08:06:18 +0100 Subject: [PATCH 04/80] rules first attempt --- rowers/models.py | 43 ++++++++++++++++++- rowers/permissions.py | 2 +- rowers/teams.py | 85 +++++++++++++++++++------------------- rowers/views/statements.py | 2 +- rowers/views/teamviews.py | 74 +++++++++++++++++---------------- rowsandall_app/settings.py | 2 + 6 files changed, 126 insertions(+), 82 deletions(-) diff --git a/rowers/models.py b/rowers/models.py index 6d9b7ac9..88b06f95 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -39,6 +39,9 @@ import pandas as pd from dateutil import parser import datetime +import rules +from rules.contrib.models import RulesModel + from rowers.rows import validate_file_extension from collections import OrderedDict from timezonefinder import TimezoneFinder @@ -53,6 +56,37 @@ from rowsandall_app.settings import ( TWEET_CONSUMER_SECRET, ) +# PERMISSIONS + +@rules.predicate +def is_team_manager(user,team): + print('aap') + return team.manager == user + +@rules.predicate +def is_team_member(user,team): + members = Rower.objects.filter(team__in=[team]) + return user in [member.user for member in members] + +@rules.predicate +def is_coach(user): + r = Rower.objects.get(user=user) + return r.rowerplan in ['coach','freecoach'] + +@rules.predicate +def can_view_team(user,team): + # user based + r = Rower.objects.get(user=user) + if r.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach': + return False + # team is public + if team.private == 'open': + return True + # team is private + return is_team_member(user,team) | is_team_manager(user,team) + +# END PERMISSIONS + tweetapi = twitter.Api(consumer_key=TWEET_CONSUMER_KEY, consumer_secret=TWEET_CONSUMER_SECRET, access_token_key=TWEET_ACCESS_TOKEN_KEY, @@ -331,7 +365,7 @@ def is_not_basic(user): # For future Team functionality @python_2_unicode_compatible -class Team(models.Model): +class Team(RulesModel): choices = ( ('private','private'), ('open','open'), @@ -350,6 +384,13 @@ class Team(models.Model): viewing = models.CharField(max_length=30,choices=viewchoices,default='allmembers',verbose_name='Sharing Behavior') + class Meta: + rules_permissions = { + "add": is_coach, + "change": is_team_manager, + "delete": is_team_manager, + "view": can_view_team, + } def __str__(self): return self.name diff --git a/rowers/permissions.py b/rowers/permissions.py index 2d32add8..4950f910 100644 --- a/rowers/permissions.py +++ b/rowers/permissions.py @@ -22,7 +22,7 @@ class IsOwnerOrReadOnly(permissions.BasePermission): return obj.user == request.user class IsOwnerOrNot(permissions.BasePermission): - + def has_object_permission(self, request, view, obj): r = Rower.objects.get(user=request.user) return (obj.user == r) diff --git a/rowers/teams.py b/rowers/teams.py index 4c8ee0a4..2556cf21 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -23,7 +23,7 @@ queuehigh = django_rq.get_queue('low') from rowers.models import ( Rower, Workout, Team, TeamInvite,User,TeamRequest, CoachRequest, CoachOffer, - CoachingGroup + CoachingGroup,is_team_manager,is_team_member,is_coach ) from rowers.tasks import ( @@ -53,14 +53,15 @@ def handle_remove_workouts_team(ws,t): return 1 def handle_add_workouts_team(ws,t): - + for w in ws: w.team.add(t) return 1 def update_team(t,name,manager,private,notes,viewing): - if t.manager != manager: + + if not is_team_manager(t,manager): return (0,'You are not the manager of this team') try: t.name = name @@ -82,7 +83,7 @@ def create_team(name,manager,private='open',notes='',viewing='allmembers'): ts = Team.objects.filter(manager=manager) if len(ts)>=1: return (0,'You need to upgrade to the Coach plan to have more than one team') - + try: t = Team(name=name,manager=manager,notes=notes, private=private,viewing=viewing) @@ -116,7 +117,7 @@ def remove_team(id): def add_coach(coach,rower): # get coaching group - + coachgroup = coach.mycoachgroup if coachgroup is None: coachgroup = CoachingGroup(name=coach.user.first_name) @@ -126,7 +127,7 @@ def add_coach(coach,rower): if get_coach_club_size(coach) invitation.issuedate+timedelta(days=inviteduration): revoke_invite(invitation.team.manager,invitation.id) return (0,'The invitation has expired') - + t = invitation.team result, comment = add_member(t.id,r) if not result: @@ -698,7 +699,7 @@ def process_coachoffer_code(user,code): return result else: send_coachoffer_accepted_email(rekwest) - + rekwest.delete() return result @@ -732,7 +733,7 @@ def send_coachrequest_accepted_email(rekwest): res = myqueue(queuehigh, handle_sendemail_coachrequest_accepted, email,coachname,name) - + def send_coachoffer_accepted_email(rekwest): coachname = rekwest.coach.user.first_name + " " + rekwest.coach.user.last_name @@ -743,5 +744,3 @@ def send_coachoffer_accepted_email(rekwest): res = myqueue(queuehigh, handle_sendemail_coachoffer_accepted, coachemail,coachname,name) - - diff --git a/rowers/views/statements.py b/rowers/views/statements.py index aab50741..a14d41a4 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -240,7 +240,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): diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 36c6b85b..c767f0d0 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -6,7 +6,8 @@ from __future__ import unicode_literals from rowers.views.statements import * -@login_required() +#@login_required() +@permission_required('teams.view_team',fn=objectgetter(Team,'id')) def team_view(request,id=0,userid=0): ismember = 0 hasrequested = 0 @@ -16,25 +17,25 @@ def team_view(request,id=0,userid=0): teams.remove_expired_invites() - + 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 +60,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 +69,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): @@ -112,7 +113,7 @@ 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), @@ -144,7 +145,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 +156,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 +165,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 +192,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 +209,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 +231,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) Date: Sat, 11 Jan 2020 15:23:26 +0100 Subject: [PATCH 05/80] now working with permissions defined in models.py --- requirements.txt | 3 ++- rowers/models.py | 19 ++++++++++--------- rowers/teams.py | 10 +++++----- rowers/tests/test_permissions.py | 2 +- rowers/urls.py | 4 ++-- rowers/views/statements.py | 1 + rowers/views/teamviews.py | 32 +++++++++++++++++--------------- 7 files changed, 38 insertions(+), 33 deletions(-) diff --git a/requirements.txt b/requirements.txt index 58eb6402..05616a35 100644 --- a/requirements.txt +++ b/requirements.txt @@ -173,9 +173,10 @@ ratelim==0.1.6 redis==3.2.1 requests==2.21.0 requests-oauthlib==1.2.0 -rowingdata==2.5.7 +rowingdata==2.5.9 rowingphysics==0.5.0 rq==0.13.0 +rules==2.1 scipy==1.2.1 SecretStorage==3.1.1 Send2Trash==1.5.0 diff --git a/rowers/models.py b/rowers/models.py index 88b06f95..5edf81e7 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -60,7 +60,6 @@ from rowsandall_app.settings import ( @rules.predicate def is_team_manager(user,team): - print('aap') return team.manager == user @rules.predicate @@ -85,6 +84,12 @@ def can_view_team(user,team): # team is private return is_team_member(user,team) | is_team_manager(user,team) +# For Team functionality +rules.add_perm('teams.view_team',can_view_team) +rules.add_perm('teams.add_team',is_coach) +rules.add_perm('teams.change_team',is_team_manager) +rules.add_perm('teams.delete_team',is_team_manager) + # END PERMISSIONS tweetapi = twitter.Api(consumer_key=TWEET_CONSUMER_KEY, @@ -363,7 +368,9 @@ def is_not_basic(user): ) -# For future Team functionality + + + @python_2_unicode_compatible class Team(RulesModel): choices = ( @@ -384,13 +391,7 @@ class Team(RulesModel): viewing = models.CharField(max_length=30,choices=viewchoices,default='allmembers',verbose_name='Sharing Behavior') - class Meta: - rules_permissions = { - "add": is_coach, - "change": is_team_manager, - "delete": is_team_manager, - "view": can_view_team, - } + def __str__(self): return self.name diff --git a/rowers/teams.py b/rowers/teams.py index 2556cf21..6c3b681d 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -61,7 +61,7 @@ def handle_add_workouts_team(ws,t): def update_team(t,name,manager,private,notes,viewing): - if not is_team_manager(t,manager): + if not is_team_manager(manager,t): return (0,'You are not the manager of this team') try: t.name = name @@ -214,7 +214,7 @@ def coach_remove_athlete(coach,rower): def mgr_remove_member(id,manager,rower): t = Team.objects.get(id=id) - if is_team_manager(t,manager): + if is_team_manager(manager,t): remove_member(id,rower) send_email_member_dropped(id,rower) return (id,'Member removed') @@ -346,7 +346,7 @@ def create_coaching_offer(coach,user): def create_invite(team,manager,user=None,email=''): r = Rower.objects.get(user=manager) - if not is_team_manager(team,manager): + if not is_team_manager(manager,team): return (0,'Not the team manager') if user: try: @@ -434,7 +434,7 @@ def revoke_invite(manager,id): except TeamInvite.DoesNotExist: return (0,'The invitation is invalid') - if is_team_manager(invite.team,manager): + if is_team_manager(manager,invite.team): invite.delete() return (1,'Invitation revoked') else: @@ -446,7 +446,7 @@ def reject_request(manager,id): except TeamRequest.DoesNotExist: return (0,'The request is invalid') - if is_team_manager(rekwest.team,manager): + if is_team_manager(manager,rekwest.team): send_request_reject_email(rekwest) rekwest.delete() return (1,'Request rejected') diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index 2213a7b8..bd8cc51d 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -1180,7 +1180,7 @@ class PermissionsViewTests(TestCase): response = self.c.get(url,follow=True) self.assertEqual(response.status_code,200) - expected_url = reverse('team_view',kwargs={'id':self.teampro.id}) + expected_url = reverse('team_view',kwargs={'team_id':self.teampro.id}) self.assertRedirects(response, expected_url = expected_url, diff --git a/rowers/urls.py b/rowers/urls.py index 7146d4da..da05dc02 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -458,12 +458,12 @@ urlpatterns = [ re_path(r'^me/calcdps/$',views.rower_calcdps_view,name='rower_calcdps_view'), re_path(r'^me/exportsettings/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), re_path(r'^me/exportsettings/user/(?P\d+)/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), - re_path(r'^team/(?P\d+)/$',views.team_view,name='team_view'), + re_path(r'^team/(?P\d+)/$',views.team_view,name='team_view'), re_path(r'^team/(?P\d+)/memberstats/$',views.team_members_stats_view,name='team_members_stats_view'), re_path(r'^team/(?P\d+)/edit/$',views.team_edit_view,name='team_edit_view'), re_path(r'^team/(?P\d+)/leaveconfirm/$',views.team_leaveconfirm_view,name='team_leaveconfirm_view'), re_path(r'^team/(?P\d+)/leave/$',views.team_leave_view,name='team_leave_view'), - re_path(r'^team/(?P\d+)/deleteconfirm/$',views.team_deleteconfirm_view,name='team_deleteconfirm_view'), + re_path(r'^team/(?P\d+)/deleteconfirm/$',views.team_deleteconfirm_view,name='team_deleteconfirm_view'), re_path(r'^team/(?P\d+)/requestmembership/(?P\d+)/$',views.team_requestmembership_view,name='team_requestmembership_view'), re_path(r'^me/coachrequest/(?P\d+)/reject/$',views.reject_revoke_coach_request, name='reject_revoke_coach_request'), diff --git a/rowers/views/statements.py b/rowers/views/statements.py index a14d41a4..fa81fc24 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 diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index c767f0d0..4d9b1b9f 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -5,10 +5,12 @@ from __future__ import unicode_literals from rowers.views.statements import * +def get_team_by_pk(request,team_id): + return get_object_or_404(Team,pk=team_id) #@login_required() -@permission_required('teams.view_team',fn=objectgetter(Team,'id')) -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) @@ -19,7 +21,7 @@ def team_view(request,id=0,userid=0): try: - t = Team.objects.get(id=id) + t = Team.objects.get(id=team_id) except Team.DoesNotExist: raise Http404("Team doesn't exist") @@ -84,7 +86,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 } ] @@ -120,7 +122,7 @@ def team_leaveconfirm_view(request,id=0): 'name': 'Groups' }, { - 'url':reverse(team_view,kwargs={'id':id}), + 'url':reverse(team_view,kwargs={'team_id':id}), 'name': t.name }, { @@ -451,7 +453,7 @@ def team_requestmembership_view(request,teamid,userid): url = reverse('team_view',kwargs={ - 'id':int(teamid), + 'team_id':int(teamid), }) @@ -572,7 +574,7 @@ def rower_invitations_view(request,code=None,message='',successmessage=''): messages.info(request,text) teamid=res url = reverse(team_view,kwargs={ - 'id':teamid, + 'team_id':teamid, }) else: messages.error(request,text) @@ -610,7 +612,7 @@ def team_edit_view(request,id=0): url = reverse(team_view, kwargs={ - 'id':int(id), + 'team_id':int(id), } ) @@ -628,7 +630,7 @@ def team_edit_view(request,id=0): 'name': 'Groups' }, { - 'url':reverse(team_view,kwargs={'id':id}), + 'url':reverse(team_view,kwargs={'team_id':id}), 'name': t.name }, { @@ -710,11 +712,11 @@ def team_create_view(request): }) #@login_required() -@permission_required('teams.delete_team',fn=objectgetter(Team,'id')) -def team_deleteconfirm_view(request,id): +@permission_required('teams.delete_team',fn=get_team_by_pk) +def team_deleteconfirm_view(request,team_id): r = getrower(request.user) try: - t = Team.objects.get(id=id) + t = Team.objects.get(id=team_id) except Team.DoesNotExist: raise Http404("This team doesn't exist") if t.manager != request.user: @@ -728,11 +730,11 @@ def team_deleteconfirm_view(request,id): 'name': 'Groups' }, { - 'url':reverse(team_view,kwargs={'id':id}), + 'url':reverse(team_view,kwargs={'team_id':team_id}), 'name': t.name }, { - 'url':reverse(team_deleteconfirm_view,kwargs={'id':id}), + 'url':reverse(team_deleteconfirm_view,kwargs={'team_id':team_id}), 'name': 'Leave' } ] @@ -784,7 +786,7 @@ def team_members_stats_view(request,id): 'name': 'Groups' }, { - 'url':reverse(team_view,kwargs={'id':id}), + 'url':reverse(team_view,kwargs={'team_id':id}), 'name': t.name }, { From ad9cab43927f9ad18a6559663b2ad9a32397e852 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 11 Jan 2020 16:38:53 +0100 Subject: [PATCH 06/80] rules now separately in rower_rules --- rowers/models.py | 42 ++++----------------------------------- rowers/rower_rules.py | 33 ++++++++++++++++++++++++++++++ rowers/teams.py | 4 +++- rowers/views/teamviews.py | 2 ++ 4 files changed, 42 insertions(+), 39 deletions(-) create mode 100644 rowers/rower_rules.py diff --git a/rowers/models.py b/rowers/models.py index 5edf81e7..593dfd1e 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -39,8 +39,9 @@ import pandas as pd from dateutil import parser import datetime -import rules -from rules.contrib.models import RulesModel +#from rules.contrib.models import RulesModel + + from rowers.rows import validate_file_extension from collections import OrderedDict @@ -56,39 +57,7 @@ from rowsandall_app.settings import ( TWEET_CONSUMER_SECRET, ) -# PERMISSIONS -@rules.predicate -def is_team_manager(user,team): - return team.manager == user - -@rules.predicate -def is_team_member(user,team): - members = Rower.objects.filter(team__in=[team]) - return user in [member.user for member in members] - -@rules.predicate -def is_coach(user): - r = Rower.objects.get(user=user) - return r.rowerplan in ['coach','freecoach'] - -@rules.predicate -def can_view_team(user,team): - # user based - r = Rower.objects.get(user=user) - if r.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach': - return False - # team is public - if team.private == 'open': - return True - # team is private - return is_team_member(user,team) | is_team_manager(user,team) - -# For Team functionality -rules.add_perm('teams.view_team',can_view_team) -rules.add_perm('teams.add_team',is_coach) -rules.add_perm('teams.change_team',is_team_manager) -rules.add_perm('teams.delete_team',is_team_manager) # END PERMISSIONS @@ -368,11 +337,8 @@ def is_not_basic(user): ) - - - @python_2_unicode_compatible -class Team(RulesModel): +class Team(models.Model): choices = ( ('private','private'), ('open','open'), diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py new file mode 100644 index 00000000..0b89468a --- /dev/null +++ b/rowers/rower_rules.py @@ -0,0 +1,33 @@ +import rules + +# PERMISSIONS + +@rules.predicate +def is_team_manager(user,team): + return team.manager == user + +@rules.predicate +def is_team_member(user,team): + members = team.rower.all() + return user in [member.user for member in members] + +@rules.predicate +def is_coach(user): + return user.rower.rowerplan in ['coach','freecoach'] + +@rules.predicate +def can_view_team(user,team): + # user based + if user.rower.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach': + return False + # team is public + if team.private == 'open': + return True + # team is private + return is_team_member(user,team) | is_team_manager(user,team) + +# For Team functionality +rules.add_perm('teams.view_team',can_view_team) +rules.add_perm('teams.add_team',is_coach) +rules.add_perm('teams.change_team',is_team_manager) +rules.add_perm('teams.delete_team',is_team_manager) diff --git a/rowers/teams.py b/rowers/teams.py index 6c3b681d..e487c5ea 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -23,9 +23,11 @@ queuehigh = django_rq.get_queue('low') from rowers.models import ( Rower, Workout, Team, TeamInvite,User,TeamRequest, CoachRequest, CoachOffer, - CoachingGroup,is_team_manager,is_team_member,is_coach + CoachingGroup ) +from rowers.rower_rules import is_team_manager,is_team_member,is_coach + from rowers.tasks import ( handle_sendemail_invite, handle_sendemail_request, diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 4d9b1b9f..73108d9d 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -5,6 +5,8 @@ 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) From 48a361a43e735ceeccc235384ad2d081b0d1aab4 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 11 Jan 2020 17:05:08 +0100 Subject: [PATCH 07/80] passing tests --- rowers/urls.py | 6 ++-- rowers/views/teamviews.py | 58 ++++++++++++--------------------------- 2 files changed, 21 insertions(+), 43 deletions(-) diff --git a/rowers/urls.py b/rowers/urls.py index da05dc02..6a061af4 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -459,8 +459,8 @@ urlpatterns = [ re_path(r'^me/exportsettings/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), re_path(r'^me/exportsettings/user/(?P\d+)/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), re_path(r'^team/(?P\d+)/$',views.team_view,name='team_view'), - re_path(r'^team/(?P\d+)/memberstats/$',views.team_members_stats_view,name='team_members_stats_view'), - re_path(r'^team/(?P\d+)/edit/$',views.team_edit_view,name='team_edit_view'), + re_path(r'^team/(?P\d+)/memberstats/$',views.team_members_stats_view,name='team_members_stats_view'), + re_path(r'^team/(?P\d+)/edit/$',views.team_edit_view,name='team_edit_view'), re_path(r'^team/(?P\d+)/leaveconfirm/$',views.team_leaveconfirm_view,name='team_leaveconfirm_view'), re_path(r'^team/(?P\d+)/leave/$',views.team_leave_view,name='team_leave_view'), re_path(r'^team/(?P\d+)/deleteconfirm/$',views.team_deleteconfirm_view,name='team_deleteconfirm_view'), @@ -489,7 +489,7 @@ urlpatterns = [ name='coach_accept_coachrequest_view'), re_path(r'^me/coachoffer/(?P\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'), diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 73108d9d..67315e2c 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -20,12 +20,7 @@ def team_view(request,team_id=0,userid=0): myteams, memberteams, otherteams = get_teams(request) teams.remove_expired_invites() - - - try: - t = Team.objects.get(id=team_id) - except Team.DoesNotExist: - raise Http404("Team doesn't exist") + t = get_object_or_404(Team,pk=team_id) #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") @@ -589,12 +584,10 @@ def rower_invitations_view(request,code=None,message='',successmessage=''): }) return HttpResponseRedirect(url) -@login_required() -def team_edit_view(request,id=0): - try: - t = Team.objects.get(id=id) - except Team.DoesNotExist: - raise Http404("Team does not exist") +@permission_required('teams.change_team',fn=get_team_by_pk,raise_exception=True) +def team_edit_view(request, team_id=0): + t = get_object_or_404(Team,pk=team_id) + if request.method == 'POST': teamcreateform = TeamForm(request.POST,instance=t) @@ -614,7 +607,7 @@ def team_edit_view(request,id=0): url = reverse(team_view, kwargs={ - 'team_id':int(id), + 'team_id':int(team_id), } ) @@ -632,11 +625,11 @@ def team_edit_view(request,id=0): 'name': 'Groups' }, { - 'url':reverse(team_view,kwargs={'team_id':id}), + 'url':reverse(team_view,kwargs={'team_id':team_id}), 'name': t.name }, { - 'url':reverse(team_edit_view,kwargs={'id':id}), + 'url':reverse(team_edit_view,kwargs={'team_id':team_id}), 'name': 'Edit' } ] @@ -714,15 +707,10 @@ def team_create_view(request): }) #@login_required() -@permission_required('teams.delete_team',fn=get_team_by_pk) +@permission_required('teams.delete_team',fn=get_team_by_pk,raise_exception=True) def team_deleteconfirm_view(request,team_id): r = getrower(request.user) - try: - t = Team.objects.get(id=team_id) - except Team.DoesNotExist: - raise Http404("This team doesn't exist") - if t.manager != request.user: - raise PermissionDenied("You are not allowed to delete this team") + t = get_object_or_404(Team,pk=team_id) myteams, memberteams, otherteams = get_teams(request) @@ -750,15 +738,10 @@ def team_deleteconfirm_view(request,team_id): 'active':'nav-teams', }) -@login_required() -def team_delete_view(request,id): +@permission_required('teams.delete_team',fn=get_team_by_pk,raise_exception=True) +def team_delete_view(request,team_id): r = getrower(request.user) - try: - t = Team.objects.get(id=id) - except Team.DoesNotExist: - raise Http404("This team doesn't exist") - if t.manager != request.user: - raise PermissionDenied("You are not allowed to delete this team") + t = get_object_or_404(Team,pk=team_id) teams.remove_team(t.id) @@ -766,15 +749,10 @@ def team_delete_view(request,id): response = HttpResponseRedirect(url) return response -@login_required() -def team_members_stats_view(request,id): +@permission_required('teams.change_team',fn=get_team_by_pk,raise_exception=True) +def team_members_stats_view(request,team_id): r = getrower(request.user) - try: - t = Team.objects.get(id=id) - except Team.DoesNotExist: - raise Http404("This team doesn't exist") - if t.manager != request.user: - raise PermissionDenied("You are not allowed to see this page") + t = get_object_or_404(Team,pk=team_id) members = Rower.objects.filter(team=t).order_by("user__last_name","user__first_name") @@ -788,11 +766,11 @@ def team_members_stats_view(request,id): 'name': 'Groups' }, { - 'url':reverse(team_view,kwargs={'team_id':id}), + 'url':reverse(team_view,kwargs={'team_id':team_id}), 'name': t.name }, { - 'url':reverse(team_members_stats_view,kwargs={'id':id}), + 'url':reverse(team_members_stats_view,kwargs={'team_id':team_id}), 'name': 'Members Stats' } ] From 892b6c0e606a9c86547332ab6a8d2ab45949504c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 11 Jan 2020 17:56:38 +0100 Subject: [PATCH 08/80] small changes --- rowers/rower_rules.py | 26 +++++++++++++++++++++----- rowers/teams.py | 17 ++--------------- rowers/views/teamviews.py | 12 ++++-------- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 0b89468a..386cb5eb 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -1,7 +1,26 @@ import rules +import datetime # PERMISSIONS +# USER permissions + +@rules.predicate +def user_is_not_basic(user): + if user.rower.rowerplan != 'basic': + return True + + if user.rower.protrialexpires >= datetime.date.today() and user.rower.plantrialexpires >= datetime.date.today(): + return True + + return False + +@rules.predicate +def is_coach(user): + return user.rower.rowerplan in ['coach','freecoach'] + +# TEAM permissions + @rules.predicate def is_team_manager(user,team): return team.manager == user @@ -11,10 +30,6 @@ def is_team_member(user,team): members = team.rower.all() return user in [member.user for member in members] -@rules.predicate -def is_coach(user): - return user.rower.rowerplan in ['coach','freecoach'] - @rules.predicate def can_view_team(user,team): # user based @@ -26,8 +41,9 @@ def can_view_team(user,team): # team is private return is_team_member(user,team) | is_team_manager(user,team) + # For Team functionality rules.add_perm('teams.view_team',can_view_team) -rules.add_perm('teams.add_team',is_coach) +rules.add_perm('teams.add_team',user_is_not_basic) rules.add_perm('teams.change_team',is_team_manager) rules.add_perm('teams.delete_team',is_team_manager) diff --git a/rowers/teams.py b/rowers/teams.py index e487c5ea..5e3fe126 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -81,7 +81,7 @@ def create_team(name,manager,private='open',notes='',viewing='allmembers'): if manager.rower.rowerplan == 'basic': if manager.rower.protrialexpires < timezone.now().date() and manager.rower.plantrialexpires < timezone.now().date(): return (0,'You need to upgrade to a paid plan to establish a team') - if manager.rower.rowerplan not in ('coach','freecoach'): + if not is_coach(manager): ts = Team.objects.filter(manager=manager) if len(ts)>=1: return (0,'You need to upgrade to the Coach plan to have more than one team') @@ -102,19 +102,6 @@ def remove_team(id): send_team_delete_mail(t,r) return t.delete() -#def set_teamplanexpires(rower): -# ts = Team.objects.filter(rower=rower) - -# texp = datetime.date(timezone.now()) - -# for t in ts: -# print t.name -# mr = Rower.objects.get(user=t.manager) -# if mr.teamplanexpires > texp: -# rower.teamplanexpires = mr.teamplanexpires - -# t.save() - return (1,'Updated rower team expiry') def add_coach(coach,rower): @@ -194,7 +181,7 @@ def rower_get_coaches(rower): def coach_getcoachees(coach): - if coach.mycoachgroup and coach.rowerplan in ('coach','freecoach'): + if coach.mycoachgroup and is_coach(coach.user): return Rower.objects.filter( coachinggroups__in=[coach.mycoachgroup] ).distinct().order_by("user__last_name","user__first_name") diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 67315e2c..3e9d942e 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -647,17 +647,13 @@ def team_edit_view(request, team_id=0): }) #@user_passes_test(cancreateteam,login_url="/rowers/paidplans",redirect_field_name=None) -@login_required() def team_create_view(request): r = getrequestrower(request) - if r.rowerplan == 'basic': - if r.protrialexpires < timezone.now().date() and r.plantrialexpires < timezone.now().date(): - messages.error(request,"You must upgrade to Pro or higher to create teams/training groups") - url = reverse('paidplans') - return HttpResponseRedirect(url) - - + if not user_is_not_basic(request.user): + messages.error(request,"You must upgrade to Pro or higher to create teams/training groups") + url = reverse('paidplans') + return HttpResponseRedirect(url) if request.method == 'POST': teamcreateform = TeamForm(request.POST) From c82a60f02e62ff28b674c7e89640d3ce8432c4ca Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 12 Jan 2020 17:58:55 +0100 Subject: [PATCH 09/80] moved user and workout permission checks to rules updated workoutviews, rest of views not done doesn't pass tests --- rowers/c2stuff.py | 3 +- rowers/imports.py | 25 +- rowers/models.py | 91 +---- rowers/polarstuff.py | 34 +- rowers/rower_rules.py | 148 ++++++++ rowers/runkeeperstuff.py | 4 +- rowers/sporttracksstuff.py | 3 +- rowers/stravastuff.py | 4 +- rowers/templatetags/rowerfilters.py | 7 +- rowers/tests/mocks.py | 2 +- rowers/tests/statements.py | 24 +- rowers/tests/test_urls.py | 1 - rowers/tpstuff.py | 4 +- rowers/traverselinktest.py | 6 +- rowers/underarmourstuff.py | 3 +- rowers/uploads.py | 18 +- rowers/urls.py | 2 - rowers/utils.py | 54 ++- rowers/views/exportviews.py | 31 +- rowers/views/importviews.py | 561 ++++++++++++++-------------- rowers/views/planviews.py | 11 +- rowers/views/statements.py | 76 +--- rowers/views/workoutviews.py | 217 ++++------- 23 files changed, 618 insertions(+), 711 deletions(-) diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py index 4a082f35..432b5634 100644 --- a/rowers/c2stuff.py +++ b/rowers/c2stuff.py @@ -14,6 +14,7 @@ import datetime from requests import Request, Session import rowers.mytypes as mytypes from rowers.mytypes import otwtypes +from rowers.rower_rules import is_workout_user from iso8601 import ParseError import numpy @@ -863,7 +864,7 @@ def workout_c2_upload(user,w): r = Rower.objects.get(user=user) # ready to upload. Hurray - if (checkworkoutuser(user,w)): + if (is_workout_user(user,w)): c2userid = get_userid(r.c2token) if not c2userid: raise NoTokenError("User has no token") diff --git a/rowers/imports.py b/rowers/imports.py index fe1a85e6..d3a3ab01 100644 --- a/rowers/imports.py +++ b/rowers/imports.py @@ -43,7 +43,7 @@ from django.contrib.auth.decorators import login_required # from .models import Profile from rowingdata import rowingdata, make_cumvalues import pandas as pd -from rowers.models import Rower,Workout,checkworkoutuser,TombStone +from rowers.models import Rower,Workout,TombStone import rowers.mytypes as mytypes from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, @@ -54,7 +54,7 @@ from rowsandall_app.settings import ( from rowers.utils import ( NoTokenError, custom_exception_handler, ewmovingaverage, - geo_distance,isprorower,uniqify + geo_distance,uniqify ) @@ -144,7 +144,7 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''): else: grant_type = post_data.pop('grant_type',None) - if oauth_data['bearer_auth']: + if oauth_data['bearer_auth']: headers['authorization'] = 'Bearer %s' % access_token baseurl = oauth_data['base_url'] @@ -159,7 +159,7 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''): headers=headers) - + if response.status_code == 200 or response.status_code == 201: token_json = response.json() else: @@ -186,7 +186,7 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''): expires_in = int(expires_in) except (TypeError,ValueError): expires_in = 0 - + return [thetoken,expires_in,refresh_token] @@ -199,20 +199,20 @@ def imports_get_token( client_secret = oauth_data['client_secret'] client_id = oauth_data['client_id'] base_uri = oauth_data['base_url'] - + client_auth = requests.auth.HTTPBasicAuth( client_id,client_secret ) - + post_data = {"grant_type": "authorization_code", "code": code, "redirect_uri": redirect_uri, "client_secret": client_secret, "client_id": client_id, } - + try: headers = oauth_data['headers'] except KeyError: @@ -229,7 +229,7 @@ def imports_get_token( else: grant_type = post_data.pop('grant_type',None) - + if 'json' in oauth_data['content_type']: response = requests.post( base_uri, @@ -262,7 +262,7 @@ def imports_get_token( else: return [0,0,0] - + return [thetoken,expires_in,refresh_token] # Make authorization URL including random string @@ -293,8 +293,8 @@ def imports_token_refresh(user,tokenname,refreshtokenname,expirydatename,oauth_d # for Strava transition if not refreshtoken: refreshtoken = getattr(r,tokenname) - - + + res = imports_do_refresh_token(refreshtoken,oauth_data) access_token = res[0] expires_in = res[1] @@ -309,4 +309,3 @@ def imports_token_refresh(user,tokenname,refreshtokenname,expirydatename,oauth_d r.save() return access_token - diff --git a/rowers/models.py b/rowers/models.py index 593dfd1e..c0c9c520 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -666,6 +666,9 @@ class CoachingGroup(models.Model): rs = Rower.objects.filter(coachinggroups__in=[self]) return rs.count() + def get_coaches(self): + return Rower.objects.filter(mycoachgroup=self) + # Extension of User with rowing specific data @python_2_unicode_compatible class Rower(models.Model): @@ -1173,95 +1176,7 @@ class BasePlannedSessionFormSet(BaseFormSet): return -# Check if workout is owned by this user -def checkworkoutuser(user,workout): - if user.is_anonymous: - return False - try: - r = Rower.objects.get(user=user) - if workout.user == r: - return True - coaches = [] - for group in workout.user.coachinggroups.all(): - coach = Rower.objects.get(mycoachgroup=group) - coaches.append(coach) - for coach in coaches: - if user.rower == coach and workout.privacy == 'visible': - return True - else: - return False - except Rower.DoesNotExist: - return False -# Check if workout may be viewed by this user -def checkworkoutuserview(user,workout): - if user.is_anonymous: - return False - try: - r = Rower.objects.get(user=user) - if workout.user == r: - return True - teams = workout.user.team.all() - - for team in teams: - if team in r.team.all(): - return True - return False - except Rower.DoesNotExist: - return False - - return False - -def checkviewworkouts(user,rower): - try: - r = user.rower - if rower == r: - return True - teams = Team.objects.filter(manager=user) - - if rower in Rower.objects.filter(team__in=teams): - return True - - if rower in Rower.objects.filter(coachinggroups__in=[user.rower.mycoachgroup]): - return True - - - except Rower.DoesNotExist: - return False - -# check if user is plan and rower is in his group -def checkaccessplanuser(user,rower): - try: - r = Rower.objects.get(user=user) - if rower == r: - return True - team_managers = [t.manager for t in rower.team.all() if t.manager.rower.rowerplan in ['plan','coach','freecoach']] - if user.rower.rowerplan != 'basic': - return user in team_managers - else: - return False - - return False - except Rower.DoesNotExist: - return False - -# Check if user is coach or rower -def checkaccessuser(user,rower): - try: - r = Rower.objects.get(user=user) - if rower == r: - return True - coaches = [] - for group in rower.coachinggroups.all(): - coach = Rower.objects.get(mycoachgroup=group) - coaches.append(coach) - for coach in coaches: - if user.rower == coach: - return True - else: - return False - except Rower.DoesNotExist: - return False timezones = ( (x,x) for x in pytz.common_timezones diff --git a/rowers/polarstuff.py b/rowers/polarstuff.py index dc4f397a..2466c1e7 100644 --- a/rowers/polarstuff.py +++ b/rowers/polarstuff.py @@ -38,7 +38,7 @@ from django.contrib.auth.decorators import login_required from rowingdata import rowingdata import pandas as pd from rowers.models import Rower,Workout -from rowers.models import checkworkoutuser + import rowers.dataprep as dataprep from rowers.dataprep import columndict @@ -50,7 +50,7 @@ from stravalib.exc import ActivityUploadFailed,TimeoutExceeded from django_mailbox.models import Message,Mailbox,MessageAttachment from rowsandall_app.settings import ( - POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET, + POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET, ) #baseurl = 'https://polaraccesslink.com/v3-example' @@ -79,7 +79,7 @@ def get_token(code): except TypeError: headers = { 'Authorization': 'Basic %s' % base64.b64encode( bytes(auth_string,'utf-8')).decode('utf-8') } - + response = requests.post("https://polarremote.com/v2/oauth2/token", data=post_data, headers=headers) @@ -110,7 +110,7 @@ def make_authorization_url(): import urllib url = "https://flow.polar.com/oauth2/authorization" +urllib.parse.urlencode(params) - + return HttpResponseRedirect(url) def get_polar_notifications(): @@ -130,13 +130,13 @@ def get_polar_notifications(): response = requests.get(url, headers=headers) available_data = [] - + if response.status_code == 200: available_data = response.json()['available-user-data'] - + return available_data -from rowers.utils import isprorower +from rowers.rower_rules import ispromember def get_all_new_workouts(available_data,testing=False): for record in available_data: @@ -146,7 +146,7 @@ def get_all_new_workouts(available_data,testing=False): try: r = Rower.objects.get(polaruserid=record['user-id']) u = r.user - if r.polar_auto_import and isprorower(r): + if r.polar_auto_import and ispromember(u): exercise_list = get_polar_workouts(u) if testing: print(exercise_list) @@ -154,13 +154,13 @@ def get_all_new_workouts(available_data,testing=False): pass return 1 - + def get_polar_workouts(user): r = Rower.objects.get(user=user) exercise_list = [] - + if (r.polartoken == '') or (r.polartoken is None): s = "Token doesn't exist. Need to authorize" return custom_exception_handler(401,s) @@ -195,7 +195,7 @@ def get_polar_workouts(user): uploadoptions, default_flow_style=False ) - + transactionid = response.json()['transaction-id'] url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}'.format( transactionid = transactionid, @@ -229,11 +229,11 @@ def get_polar_workouts(user): a = MessageAttachment(message=msg,document=filename[6:]) a.save() - + exercise_dict['filename'] = filename else: exercise_dict['filename'] = '' - + exercise_list.append(exercise_dict) # commit transaction @@ -280,7 +280,7 @@ def get_polar_user_info(user,physical=False): def get_polar_workout(user,id,transactionid): - + r = Rower.objects.get(user=user) if (r.polartoken == '') or (r.polartoken is None): s = "Token doesn't exist. Need to authorize" @@ -326,7 +326,7 @@ def get_polar_workout(user,id,transactionid): ) response = requests.get(url,headers = headers2) - + if response.status_code == 200: result = response.text # commit transaction @@ -337,7 +337,3 @@ def get_polar_workout(user,id,transactionid): return result return None - - - - diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 386cb5eb..258c86b3 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -19,6 +19,154 @@ def user_is_not_basic(user): def is_coach(user): return user.rower.rowerplan in ['coach','freecoach'] +@rules.predicate +def is_promember(user): + try: + r = user.rower + except AttributeError: + return False + + return r.rowerplan in ['pro','coach','plan'] + +@rules.predicate +def is_protrial(user): + try: + r = user.rower + except AttributeError: + return False + + if r.rowerplan == 'basic': + return r.protrialexpires >= datetime.date.today() + if r.rowerplan == 'freecoach': + if r.mycoachgroup is not None: + return len(r.mycoachgroup)>=4 + + return False + +ispromember = is_promember | is_protrial + +# User / Coach relationships (Rower object) + +@rules.predicate +def can_plan(user): + return user.rower.rowerplan in ['plan','coach','freecoach'] + +@rules.predicate +def is_coach_user(user,rower): + try: + r = user.rower + except AttributeError: + return False + + if rower == r: + return True + + coaches = [] + + for group in r.coachinggroups.all(): + newcoaches = group.get_coaches() + for coach in newcoaches: + coaches.append(coach) + print(coaches) + for coach in coaches: + if rower == coach: + return True + + return False + +@rules.predicate +def is_rower_team_member(user,rower): + if user.rower == rower: + return True + + teams = user.rower.team.all() + + for team in teams: + if team.private == 'open': + if team in rower.team.all(): + return True + if team.manager == rower.user: + return True + + return False + +@rules.predicate +def can_plan_user(user,rower): + try: + r = user.rower + except AttributeError: + return False + + if rower == r: + return True + + # below + team_managers = [t.manager for t in rower.team.all() and can_plan(t.manager)] + if user_is_not_basic(user): + return user in team_managers + + return False + +rules.add_perm('rower.can_plan',can_plan_user) # replaces checkaccessplanuser +rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser + + +# WORKOUT permissions + +@rules.predicate +def is_workout_user(user,workout): + if user.is_anonymous: + return False + + try: + r = user.rower + except AttributeError: + return False + + if workout.user == r: + return True + + coaches = [] + for group in workout.user.coachinggroups.all(): + coach = group.coachingrole + coaches.append(coach) + for coach in coaches: + if r == coach and workout.privacy == 'visible': + return True + + return False + +@rules.predicate +def can_view_workout(user,workout): + if user.is_anonymous: + if workout.privacy != 'private': + return True + return False + + try: + r = user.rower + except AttributeError: + return False + + teams = workout.user.team.all() + + for team in teams: + if team in r.team.all(): + return True + + return False + +rules.add_perm('workout.change_workout',is_workout_user) # replaces checkworkoutuser +rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutuserview + + + +# checkviewworkouts + +# PLANNING permissions + +# checkaccessplanuser (models.py) + # TEAM permissions @rules.predicate diff --git a/rowers/runkeeperstuff.py b/rowers/runkeeperstuff.py index 5e21b11a..4b2b3322 100644 --- a/rowers/runkeeperstuff.py +++ b/rowers/runkeeperstuff.py @@ -7,6 +7,8 @@ from __future__ import unicode_literals, absolute_import from rowers.imports import * import re +from rowers.rower_rules import is_workout_user + from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, @@ -285,7 +287,7 @@ def workout_runkeeper_upload(user,w): # ready to upload. Hurray - if (checkworkoutuser(user,w)): + if (is_workout_user(user,w)): data = createrunkeeperworkoutdata(w) if not data: message = "Data error in Runkeeper Upload" diff --git a/rowers/sporttracksstuff.py b/rowers/sporttracksstuff.py index 2828a18f..28fc3168 100644 --- a/rowers/sporttracksstuff.py +++ b/rowers/sporttracksstuff.py @@ -18,6 +18,7 @@ from rowsandall_app.settings import ( ) import rowers.mytypes as mytypes +from rowers.rower_rules import is_workout_user oauth_data = { 'client_id': SPORTTRACKS_CLIENT_ID, @@ -265,7 +266,7 @@ def workout_sporttracks_upload(user,w): thetoken = sporttracks_open(user) - if (checkworkoutuser(user,w)): + if (is_workout_user(user,w)): data = createsporttracksworkoutdata(w) if not data: message = "Data error" diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index d8663c97..37e33dbc 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -18,6 +18,8 @@ queuehigh = django_rq.get_queue('low') from rowers.dataprep import columndict +from rowers.rower_rules import is_workout_user + import stravalib from stravalib.exc import ActivityUploadFailed,TimeoutExceeded @@ -632,7 +634,7 @@ def workout_strava_upload(user,w): s = "Token doesn't exist. Need to authorize" raise NoTokenError("Your hovercraft is full of eels") else: - if (checkworkoutuser(user,w)): + if (is_workout_user(user,w)): try: tcxfile,tcxmesg = createstravaworkoutdata(w) if tcxfile: diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index b3b7b5d5..cb0ca2d2 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -21,7 +21,7 @@ from rowers.plannedsessions import ( from rowers import c2stuff, runkeeperstuff from rowers.c2stuff import c2_open from rowers.runkeeperstuff import runkeeper_open -from rowers.models import checkaccessuser +from rowers.rower_rules import is_coach_user, is_workout_user from rowers.mytypes import otwtypes from rowers.utils import NoTokenError @@ -385,14 +385,13 @@ def is_session_manager(id,user): return ps.manager == user -from rowers.models import checkworkoutuser @register.filter def may_edit(workout,request): mayedit = 0 if request.user == workout.user.user: mayedit = True - if checkworkoutuser(request.user,workout): + if is_workout_user(request.user,workout): mayedit = True return mayedit @@ -413,7 +412,7 @@ def mayeditplan(obj,request): return request.user == obj.manager.user rr = Rower.objects.get(user=request.user) - if checkaccessuser(request.user,obj.rower) and rr.rowerplan not in ['basic','pro']: + if is_coach_user(request.user,obj.rower) and rr.rowerplan not in ['basic','pro']: mayedit = True diff --git a/rowers/tests/mocks.py b/rowers/tests/mocks.py index 278c1b03..7acce340 100644 --- a/rowers/tests/mocks.py +++ b/rowers/tests/mocks.py @@ -18,7 +18,7 @@ from django.test import TestCase, Client,override_settings from django.core.management import call_command from django.utils.six import StringIO from django.test.client import RequestFactory -from rowers.views import checkworkoutuser,c2_open +from rowers.views import c2_open from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage from rowers.forms import DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail import rowers.plots as plots diff --git a/rowers/tests/statements.py b/rowers/tests/statements.py index 0276fe67..4b4e50a4 100644 --- a/rowers/tests/statements.py +++ b/rowers/tests/statements.py @@ -7,12 +7,12 @@ try: FileNotFoundError except NameError: FileNotFoundError = None - + try: OSError except NameError: OSError = None - + import pytest pytestmark = pytest.mark.django_db @@ -26,7 +26,7 @@ from django.core.management import call_command from django.core.files.uploadedfile import SimpleUploadedFile from django.utils.six import StringIO from django.test.client import RequestFactory -from rowers.views import checkworkoutuser,c2_open, multi_compare_view +from rowers.views import c2_open, multi_compare_view from rowers.forms import ( DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail, @@ -101,22 +101,22 @@ def get_random_file(filename='rowers/tests/testdata/testdata.csv',name=''): fromstring = 'test_%s_' % mod.__name__ except AttributeError: fromstring = 'none_' - + row = rdata(filename) totaldist = row.df['cum_dist'].max() totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() totaltime = totaltime+row.df.loc[row.df.index[0],' ElapsedTime (sec)'] - - + + hours = int(totaltime/3600.) minutes = int((totaltime - 3600.*hours)/60.) seconds = int(totaltime - 3600.*hours - 60.*minutes) tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) - + duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) duration = datetime.time(hour=hours,minute=minutes,second=seconds) - + workoutdate = row.rowdatetime.date() workoutstarttime = row.rowdatetime @@ -139,7 +139,7 @@ def get_random_file(filename='rowers/tests/testdata/testdata.csv',name=''): 'duration':duration, 'totaldist':totaldist, } - + return thedict class UserFactory(factory.DjangoModelFactory): @@ -168,11 +168,11 @@ class RaceFactory(factory.DjangoModelFactory): sessiontype = 'indoorrace' sessionvalue = 1 sessionmode = 'time' - + class WorkoutFactory(factory.DjangoModelFactory): class Meta: model = Workout - + name = factory.LazyAttribute(lambda _: faker.word()) notes = faker.text() startdatetime = get_random_file(name=faker.word())['startdatetime'] @@ -220,5 +220,3 @@ def cleanup(request): request.addfinalizer(remove_test_files) - - diff --git a/rowers/tests/test_urls.py b/rowers/tests/test_urls.py index 1f2eeee2..e3be6bac 100644 --- a/rowers/tests/test_urls.py +++ b/rowers/tests/test_urls.py @@ -209,7 +209,6 @@ class URLTests(TestCase): '/rowers/workout/'+encoded1+'/editintervals/', '/rowers/workout/'+encoded1+'/flexchart/', '/rowers/workout/'+encoded1+'/forcecurve/', - '/rowers/workout/'+encoded1+'/get-testscript/', '/rowers/workout/'+encoded1+'/get-thumbnails/', '/rowers/workout/'+encoded1+'/histo/', '/rowers/workout/'+encoded1+'/image/', diff --git a/rowers/tpstuff.py b/rowers/tpstuff.py index efdf9e8f..010e9f59 100644 --- a/rowers/tpstuff.py +++ b/rowers/tpstuff.py @@ -42,6 +42,8 @@ oauth_data = { 'scope':'write', } +from rowers.rower_rules import is_workout_user + # Checks if user has UnderArmour token, renews them if they are expired def tp_open(user): @@ -171,7 +173,7 @@ def workout_tp_upload(user,w): # need some code if token doesn't refresh - if (checkworkoutuser(user,w)): + if (is_workout_user(user,w)): tcxfile = createtpworkoutdata(w) if tcxfile: res,reason,status_code,headers = uploadactivity( diff --git a/rowers/traverselinktest.py b/rowers/traverselinktest.py index 546a5ccf..3856fb44 100644 --- a/rowers/traverselinktest.py +++ b/rowers/traverselinktest.py @@ -9,7 +9,7 @@ from django.test import TestCase, Client,override_settings from django.core.management import call_command from django.utils.six import StringIO from django.test.client import RequestFactory -from .views import checkworkoutuser,c2_open +from .views import c2_open from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage from rowers.forms import DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail import rowers.plots as plots @@ -55,12 +55,12 @@ class TraverseLinksTest(TestCase): name='testworkout',workouttype='On-water', user=self.r,date=nu.strftime('%Y-%m-%d'), starttime=nu.strftime('%H:%M:%S'), - duration="0:55:00",distance=8000) + duration="0:55:00",distance=8000) self.w2 = Workout.objects.create( name='testworkout 2',workouttype='On-water', user=self.r,date=nu.strftime('%Y-%m-%d'), starttime=nu.strftime('%H:%M:%S'), - duration="0:55:00",distance=8000) + duration="0:55:00",distance=8000) if self.client.login( username="superuser1", password="pwd"): if VERBOSE: diff --git a/rowers/underarmourstuff.py b/rowers/underarmourstuff.py index 2d18a21f..7d68d2c3 100644 --- a/rowers/underarmourstuff.py +++ b/rowers/underarmourstuff.py @@ -9,6 +9,7 @@ import numpy import rowers.mytypes as mytypes from rowers.mytypes import otwtypes +from rowers.rower_rules import is_workout_user from rowsandall_app.settings import ( UNDERARMOUR_CLIENT_KEY, @@ -331,7 +332,7 @@ def workout_ua_upload(user,w): # ready to upload. Hurray - if (checkworkoutuser(user,w)): + if (is_workout_user(user,w)): data = createunderarmourworkoutdata(w) # return HttpResponse(json.dumps(data)) if not data: diff --git a/rowers/uploads.py b/rowers/uploads.py index 2d63b557..b0676914 100644 --- a/rowers/uploads.py +++ b/rowers/uploads.py @@ -14,6 +14,8 @@ from rowers.tasks import ( from rowers.models import GraphImage +from rowers.rower_rules import ispromember + from PIL import Image import numpy as np @@ -472,6 +474,8 @@ import rowers.runkeeperstuff as runkeeperstuff import rowers.underarmourstuff as underarmourstuff import rowers.tpstuff as tpstuff +from rowers.rower_rules import is_promember + def set_workouttype(w,options): try: w.workouttype = options['workouttype'] @@ -500,8 +504,6 @@ def make_private(w,options): return 1 -from rowers.utils import isprorower - def do_sync(w,options): try: if options['stravaid'] != 0: @@ -511,7 +513,7 @@ def do_sync(w,options): except KeyError: pass - if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and isprorower(w.user)): + if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and ispromember(w.user)): if ('upload_to_C2' in options and not options['upload_to_C2']): pass else: @@ -521,7 +523,7 @@ def do_sync(w,options): id = 0 message = "Something went wrong with the Concept2 sync" - if ('upload_to_Strava' in options and options['upload_to_Strava']) or (w.user.strava_auto_export and isprorower(w.user)): + if ('upload_to_Strava' in options and options['upload_to_Strava']) or (w.user.strava_auto_export and ispromember(w.user)): if ('upload_to_Strava' in options and not options['upload_to_Strava']): pass else: @@ -534,7 +536,7 @@ def do_sync(w,options): message = "Please connect to Strava first" - if ('upload_to_SportTracks' in options and options['upload_to_SportTracks']) or (w.user.sporttracks_auto_export and isprorower(w.user)): + if ('upload_to_SportTracks' in options and options['upload_to_SportTracks']) or (w.user.sporttracks_auto_export and ispromember(w.user)): if ('upload_to_SportTracks' in options and not options['upload_to_SportTracks']): pass else: @@ -548,7 +550,7 @@ def do_sync(w,options): id = 0 - if ('upload_to_RunKeeper' in options and options['upload_to_RunKeeper']) or (w.user.runkeeper_auto_export and isprorower(w.user)): + if ('upload_to_RunKeeper' in options and options['upload_to_RunKeeper']) or (w.user.runkeeper_auto_export and ispromember(w.user)): if ('upload_to_RunKeeper' in options and not options['upload_to_RunKeeper']): pass else: @@ -561,7 +563,7 @@ def do_sync(w,options): message = "Please connect to Runkeeper first" id = 0 - if ('upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']) or (w.user.mapmyfitness_auto_export and isprorower(w.user)): + if ('upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']) or (w.user.mapmyfitness_auto_export and ispromember(w.user)): if ('upload_to_MapMyFitness' in options and not options['upload_to_MapMyFitness']): pass else: @@ -574,7 +576,7 @@ def do_sync(w,options): id = 0 - if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export and isprorower(w.user)): + if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export and ispromember(w.user)): if ('upload_to_TrainingPeaks' in options and not options['upload_to_TrainingPeaks']): pass else: diff --git a/rowers/urls.py b/rowers/urls.py index 6a061af4..15b7768f 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -289,8 +289,6 @@ urlpatterns = [ name='otw_use_gps'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/toggle-ranking/$',views.workout_toggle_ranking, name='workout_toggle_ranking'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/get-testscript/$',views.get_testscript, - name='get_testscript'), re_path(r'^workout/upload/team/$',views.team_workout_upload_view,name='team_workout_upload_view'), re_path(r'^workout/upload/$',views.workout_upload_view,name='workout_upload_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/histo/$',views.workout_histo_view, diff --git a/rowers/utils.py b/rowers/utils.py index d4e47993..a1258b1c 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -109,7 +109,7 @@ def get_call():

""" % (call1, call2) - + return call def absolute(request): @@ -131,7 +131,7 @@ def trcolors(r1,g1,b1,r2,g2,b2): h1,s1,v1 = colorsys.rgb_to_hsv(r1,g1,b1) h2,s2,v2 = colorsys.rgb_to_hsv(r2,g2,b2) - + return 360*h1,360*(h2-h1),s1,(s2-s1),v1,(v2-v1) palettes = { @@ -167,7 +167,7 @@ def is_ranking_piece(workout): return True elif workout.duration in rankingdurations: return True - + return False def range_to_color_hex(groupcols,palette='monochrome_blue'): @@ -176,20 +176,20 @@ def range_to_color_hex(groupcols,palette='monochrome_blue'): plt = palettes[palette] except KeyError: plt = palettes['monochrome_blue'] - + rgb = [colorsys.hsv_to_rgb((plt[0]+plt[1]*x)/360., plt[2]+plt[3]*x, plt[4]+plt[5]*x) for x in groupcols] RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb] colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] - + return colors def str2bool(v): return v.lower() in ("yes", "true", "t", "1") -def uniqify(seq, idfun=None): +def uniqify(seq, idfun=None): # order preserving if idfun is None: def idfun(x): return x @@ -222,12 +222,12 @@ def geo_distance(lat1,lon1,lat2,lon2): This is a slight underestimate but is close enough for our purposes, We're never moving more than 10 meters between trackpoints - Bearing calculation fails if one of the points is a pole. - (Hey, from the North pole you can walk South, East, North and end up + Bearing calculation fails if one of the points is a pole. + (Hey, from the North pole you can walk South, East, North and end up on the same spot!) - + """ - + # radius of our earth in km --> should be moved to settings if # rowing takes off on other planets R = 6373.0 @@ -257,7 +257,7 @@ def geo_distance(lat1,lon1,lat2,lon2): return [distance,bearing] - + def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio): pwr = abs(p0)/(1+(delta/abs(p2))) @@ -268,7 +268,7 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio): pwr *= ratio - + delta = delta.values.astype(int) cpvalues = cpvalues.values.astype(int) pwr = pwr.astype(int) @@ -276,7 +276,7 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio): res = np.sum(cpvalues>pwr) res2 = np.sum(cpvalues>pwr2) - + btdf = pd.DataFrame( { 'delta':delta[cpvalues>pwr], @@ -288,7 +288,7 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio): btdf.sort_values('delta',axis=0,inplace=True) - + return res>1,btdf,res2>1 @@ -300,7 +300,7 @@ def myqueue(queue,function,*args,**kwargs): def revoke(self): return 1 - + if settings.TESTING: return MockJob() elif settings.CELERY: @@ -310,7 +310,7 @@ def myqueue(queue,function,*args,**kwargs): else: if settings.DEBUG: kwargs['debug'] = True - + job_id = str(uuid.uuid4()) kwargs['job_id'] = job_id kwargs['jobkey'] = job_id @@ -335,7 +335,7 @@ def my_dict_from_instance(instance,model): thedict['id'] = instance.id for f in instance._meta.fields: - + fname = f.name try: @@ -405,24 +405,13 @@ def totaltime_sec_to_string(totaltime): return duration -def isprorower(r): - result = False - result = r.rowerplan in ['pro','coach','plan'] - - if not result and r.protrialexpires: - result = r.rowerplan == 'basic' and r.protrialexpires >= datetime.date.today() - if not result and r.rowerplan == 'freecoach': - if r.mycoachgroup is not None: - result = len(r.mycoachgroup)>=4 - - return result def iscoach(m,r): result = False result = m in r.coaches return result - + # Exponentially weighted moving average # Used for data smoothing of the jagged data obtained by Strava # See bitbucket issue 72 @@ -433,7 +422,7 @@ def ewmovingaverage(interval,window_size): intervaldf = pd.DataFrame({'v':interval}) idf_ewma1 = intervaldf.ewm(span=window_size) idf_ewma2 = intervaldf[::-1].ewm(span=window_size) - + i_ewma1 = idf_ewma1.mean().loc[:,'v'] i_ewma2 = idf_ewma2.mean().loc[:,'v'] @@ -478,7 +467,7 @@ def custom_exception_handler(exc,message): res.json = json.dumps(response) return res - + def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high'): authorizationstring = str('Bearer ' + r.stravatoken) headers = {'Authorization': authorizationstring, @@ -503,7 +492,7 @@ def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high return np.array(data['data']) except TypeError: return None - + return None def allmonths(startdate,enddate): @@ -518,4 +507,3 @@ def allsundays(startdate,enddate): while d<=enddate: yield d d += datetime.timedelta(days=7) - diff --git a/rowers/views/exportviews.py b/rowers/views/exportviews.py index 4b5f4684..08f2e064 100644 --- a/rowers/views/exportviews.py +++ b/rowers/views/exportviews.py @@ -1,18 +1,17 @@ from rowers.views.statements import * - + # Export workout to TCX and send to user's email address -@login_required() +@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) def workout_tcxemail_view(request,id=0): r = getrower(request.user) w = get_workout(id) - if not checkworkoutuser(request.user,w): - raise PermissionDenied("Access denied") + row = rdata(w.csvfilename) @@ -31,8 +30,8 @@ def workout_tcxemail_view(request,id=0): return response - - + + @login_required() def plannedsessions_icsemail_view(request,userid=0): @@ -66,7 +65,7 @@ def plannedsessions_icsemail_view(request,userid=0): d1 = startdate.strftime("%Y%m%d"), d2 = enddate.strftime("%Y%m%d"), ) - + response['Content-Type'] = 'application/octet-stream' return response @@ -96,7 +95,7 @@ def plannedsessions_coach_icsemail_view(request,userid=0): rowers += ps.rower.filter(team__in=rteams).exclude(rowerplan='freecoach') rowers = list(set(rowers)) - + cal = Calendar() cal.add('prodid','rowsandall') cal.add('version','1.0') @@ -169,13 +168,12 @@ def course_kmldownload_view(request,id=0): # Export workout to GPX and send to user's email address -@login_required() +@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) def workout_gpxemail_view(request,id=0): r = getrower(request.user) w = get_workout(id) - if not checkworkoutuser(request.user,w): - raise PermissionDenied("Access denied") + row = rdata(w.csvfilename) @@ -233,17 +231,16 @@ def workouts_summaries_email_view(request): { 'form':form }) - + # Get Workout CSV file and send it to user's email address -@login_required() +@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) def workout_csvemail_view(request,id=0): r = getrower(request.user) w = get_workout(id) - if not checkworkoutuser(request.user,w): - raise PermissionDenied("Access denied") + rowdata = rdata(w.csvfilename) code = str(uuid4()) @@ -254,13 +251,13 @@ def workout_csvemail_view(request,id=0): df = rowdata.df df[' ElapsedTime (sec)'] = df['TimeStamp (sec)'] df['TimeStamp (sec)'] = df['TimeStamp (sec)'] + starttimeunix - + response = HttpResponse(df.to_csv()) response['Content-Disposition'] = 'attachment; filename="%s"' % filename response['Content-Type'] = 'application/octet-stream' return response - + # Get Workout CSV file and send it to user's email address @login_required() diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index 2f97d00c..7b77b68b 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -8,14 +8,14 @@ from rowers.views.statements import * import numpy def default(o): - if isinstance(o, numpy.int64): return int(o) + if isinstance(o, numpy.int64): return int(o) raise TypeError # Send workout to TP -@login_required() +@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) def workout_tp_upload_view(request,id=0): - + message = "" r = getrower(request.user) res = -1 @@ -25,52 +25,48 @@ def workout_tp_upload_view(request,id=0): return HttpResponseRedirect("/rowers/me/tpauthorize/") # ready to upload. Hurray - w = get_workout_permitted(request.user,id) + w = get_object_or_404(Workout,pk=id) r = w.user - if (checkworkoutuser(request.user,w)): - tcxfile = tpstuff.createtpworkoutdata(w) - if tcxfile: - res,reason,status_code,headers = tpstuff.uploadactivity( - r.tptoken,tcxfile, - name=w.name - ) - if res == 0: - message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason - try: - os.remove(tcxfile) - except WindowsError: - pass - messages.error(request,message) - - else: # res != 0 - w.uploadedtotp = res - w.save() + tcxfile = tpstuff.createtpworkoutdata(w) + if tcxfile: + res,reason,status_code,headers = tpstuff.uploadactivity( + r.tptoken,tcxfile, + name=w.name + ) + if res == 0: + message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason + try: os.remove(tcxfile) - messages.info(request,'Uploaded to TrainingPeaks') + except WindowsError: + pass - else: # no tcxfile - message = "Upload to TrainingPeaks failed" - w.uploadedtotp = -1 - w.save() messages.error(request,message) - else: # not allowed to upload - message = "You are not allowed to export this workout to TP" + else: # res != 0 + w.uploadedtotp = res + w.save() + os.remove(tcxfile) + messages.info(request,'Uploaded to TrainingPeaks') + + else: # no tcxfile + message = "Upload to TrainingPeaks failed" + w.uploadedtotp = -1 + w.save() messages.error(request,message) url = reverse(r.defaultlandingpage, kwargs = { 'id':encoder.encode_hex(w.id), }) - + return HttpResponseRedirect(url) - - + + # Send workout to Strava # abundance of error logging here because there were/are some bugs -@login_required() +@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) def workout_strava_upload_view(request,id=0): message = "" r = getrower(request.user) @@ -80,7 +76,7 @@ def workout_strava_upload_view(request,id=0): thetoken = strava_open(request.user) except NoTokenError: return HttpResponseRedirect("/rowers/me/stravaauthorize/") - + if (r.stravatoken == '') or (r.stravatoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/stravaauthorize/") @@ -88,94 +84,94 @@ def workout_strava_upload_view(request,id=0): # ready to upload. Hurray w = get_workout_permitted(request.user,id) r = w.user - if (checkworkoutuser(request.user,w)): - try: - tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w) - if tcxfile: - with open(tcxfile,'rb') as f: + + try: + tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w) + if tcxfile: + with open(tcxfile,'rb') as f: + try: + newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' + except TypeError: + newnotes = 'from '+w.workoutsource+' via rowsandall.com' + if w.workouttype in mytypes.rowtypes: + activity_type = r.stravaexportas + else: try: - newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' - except TypeError: - newnotes = 'from '+w.workoutsource+' via rowsandall.com' - if w.workouttype in mytypes.rowtypes: - activity_type = r.stravaexportas - else: - try: - activity_type = mytypes.stravamapping[w.workouttype] - except KeyError: - activity_type = 'Ride' - - res,mes = stravastuff.handle_stravaexport( - f,w.name, - r.stravatoken, - description=newnotes, - activity_type=activity_type) - if res==0: - messages.error(request,mes) - w.uploadedtostrava = -1 - w.save() - try: - os.remove(tcxfile) - except WindowsError: - pass - url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), - }) - response = HttpResponseRedirect(url) - return response - + activity_type = mytypes.stravamapping[w.workouttype] + except KeyError: + activity_type = 'Ride' + + res,mes = stravastuff.handle_stravaexport( + f,w.name, + r.stravatoken, + description=newnotes, + activity_type=activity_type) + if res==0: + messages.error(request,mes) + w.uploadedtostrava = -1 + w.save() try: - w.uploadedtostrava = res - w.save() - try: - os.remove(tcxfile) - except WindowsError: - pass - url = reverse('workout_edit_view',kwargs={'id':w.id}) + os.remove(tcxfile) + except WindowsError: + pass + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':encoder.encode_hex(w.id), + }) + response = HttpResponseRedirect(url) + return response + + try: + w.uploadedtostrava = res + w.save() + try: + os.remove(tcxfile) + except WindowsError: + pass + url = reverse('workout_edit_view',kwargs={'id':w.id}) - messages.info(request,mes) - except: - with open("media/stravaerrors.log","a") as errorlog: - errorstring = str(sys.exc_info()[0]) - timestr = strftime("%Y%m%d-%H%M%S") - errorlog.write(timestr+errorstring+"\r\n") - errorlog.write("views.py line 826\r\n") - message = 'Error: '+errorstring - messages.error(request,message) - else: # No tcxfile - message = "Strava Data error "+tcxmessg - messages.error(request,message) - w.uploadedtostrava = -1 - w.save() - url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), - }) - response = HttpResponseRedirect(url) - - - url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), - } - ) - response = HttpResponseRedirect(url) - except ActivityUploadFailed as e: - message = "Strava Upload error: %s" % e + messages.info(request,mes) + except: + with open("media/stravaerrors.log","a") as errorlog: + errorstring = str(sys.exc_info()[0]) + timestr = strftime("%Y%m%d-%H%M%S") + errorlog.write(timestr+errorstring+"\r\n") + errorlog.write("views.py line 826\r\n") + message = 'Error: '+errorstring + messages.error(request,message) + else: # No tcxfile + message = "Strava Data error "+tcxmessg messages.error(request,message) w.uploadedtostrava = -1 w.save() - os.remove(tcxfile) url = reverse(r.defaultlandingpage, kwargs = { 'id':encoder.encode_hex(w.id), - }) + }) response = HttpResponseRedirect(url) - return response - + + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':encoder.encode_hex(w.id), + } + ) + response = HttpResponseRedirect(url) + except ActivityUploadFailed as e: + message = "Strava Upload error: %s" % e + messages.error(request,message) + w.uploadedtostrava = -1 + w.save() + os.remove(tcxfile) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':encoder.encode_hex(w.id), + }) + response = HttpResponseRedirect(url) + + return response + # Upload workout to Concept2 logbook @login_required() def workout_c2_upload_view(request,id=0): @@ -183,7 +179,7 @@ def workout_c2_upload_view(request,id=0): # ready to upload. Hurray w = get_workout(id) r = w.user - + try: message,c2id = c2stuff.workout_c2_upload(request.user,w) except NoTokenError: @@ -199,14 +195,14 @@ def workout_c2_upload_view(request,id=0): kwargs = { 'id':encoder.encode_hex(w.id) }) - + response = HttpResponseRedirect(url) return response # Upload workout to RunKeeper -@login_required() +@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) def workout_runkeeper_upload_view(request,id=0): message = "" w = get_workout(id) @@ -219,48 +215,45 @@ def workout_runkeeper_upload_view(request,id=0): # ready to upload. Hurray - if (checkworkoutuser(request.user,w)): - data = runkeeperstuff.createrunkeeperworkoutdata(w) - if not data: - message = "Data error" - messages.error(request,message) - url = reverse(r.defaultlandingpage, - kwargs = { - 'id':id, - }) - return HttpResponseRedirect(url) - - authorizationstring = str('Bearer ' + thetoken) - headers = {'Authorization': authorizationstring, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json', - 'Content-Length':'nnn'} - url = "https://api.runkeeper.com/fitnessActivities" - response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) - - # check for duplicate error first - if (response.status_code == 409 ): - message = "Duplicate error" - messages.error(request,message) - w.uploadedtorunkeeper = -1 - w.save() - elif (response.status_code == 201 or response.status_code==200): - runkeeperid = runkeeperstuff.getidfromresponse(response) - w.uploadedtorunkeeper = runkeeperid - w.save() - url = reverse('workout_edit_view', - kwargs={'id':encoder.encode_hex(w.id)}) - - return HttpResponseRedirect(url) - else: - s = response - message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text) - messages.error(request,message) - - else: - message = "You are not authorized to upload this workout" + data = runkeeperstuff.createrunkeeperworkoutdata(w) + if not data: + message = "Data error" messages.error(request,message) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':id, + }) + return HttpResponseRedirect(url) + + authorizationstring = str('Bearer ' + thetoken) + headers = {'Authorization': authorizationstring, + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json', + 'Content-Length':'nnn'} + + url = "https://api.runkeeper.com/fitnessActivities" + response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) + + # check for duplicate error first + if (response.status_code == 409 ): + message = "Duplicate error" + messages.error(request,message) + w.uploadedtorunkeeper = -1 + w.save() + elif (response.status_code == 201 or response.status_code==200): + runkeeperid = runkeeperstuff.getidfromresponse(response) + w.uploadedtorunkeeper = runkeeperid + w.save() + url = reverse('workout_edit_view', + kwargs={'id':encoder.encode_hex(w.id)}) + + return HttpResponseRedirect(url) + else: + s = response + message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text) + messages.error(request,message) + url = reverse(r.defaultlandingpage, kwargs = { @@ -270,7 +263,7 @@ def workout_runkeeper_upload_view(request,id=0): return HttpResponseRedirect(url) # Upload workout to Underarmour -@login_required() +@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) def workout_underarmour_upload_view(request,id=0): message = "" w = get_workout(id) @@ -282,48 +275,45 @@ def workout_underarmour_upload_view(request,id=0): return HttpResponseRedirect("/rowers/me/underarmourauthorize/") # ready to upload. Hurray - - if (checkworkoutuser(request.user,w)): - data = underarmourstuff.createunderarmourworkoutdata(w) - if not data: - message = "Data error" - messages.error(request,message) - url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), - }) - return HttpResponseRedirect(url) - - authorizationstring = str('Bearer ' + thetoken) - headers = {'Authorization': authorizationstring, - 'Api-Key': UNDERARMOUR_CLIENT_KEY, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json', - } - url = "https://api.ua.com/v7.1/workout/" - response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) - - # check for duplicate error first - if (response.status_code == 409 ): - message = "Duplicate error" - messages.error(request,message) - w.uploadedtounderarmour = -1 - w.save() - elif (response.status_code == 201 or response.status_code==200): - underarmourid = underarmourstuff.getidfromresponse(response) - w.uploadedtounderarmour = underarmourid - w.save() - url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)}) + data = underarmourstuff.createunderarmourworkoutdata(w) + if not data: + message = "Data error" + messages.error(request,message) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':encoder.encode_hex(w.id), + }) + return HttpResponseRedirect(url) - return HttpResponseRedirect(url) - else: - s = response - message = "Something went wrong in workout_underarmour_upload_view: %s " % s.reason - messages.error(request,message) + authorizationstring = str('Bearer ' + thetoken) + headers = {'Authorization': authorizationstring, + 'Api-Key': UNDERARMOUR_CLIENT_KEY, + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json', + } + + url = "https://api.ua.com/v7.1/workout/" + response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) + + + # check for duplicate error first + if (response.status_code == 409 ): + message = "Duplicate error" + messages.error(request,message) + w.uploadedtounderarmour = -1 + w.save() + elif (response.status_code == 201 or response.status_code==200): + underarmourid = underarmourstuff.getidfromresponse(response) + w.uploadedtounderarmour = underarmourid + w.save() + url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)}) + + return HttpResponseRedirect(url) else: - message = "You are not authorized to upload this workout" + s = response + message = "Something went wrong in workout_underarmour_upload_view: %s " % s.reason messages.error(request,message) url = reverse(r.defaultlandingpage, @@ -334,7 +324,7 @@ def workout_underarmour_upload_view(request,id=0): return HttpResponseRedirect(url) # Upload workout to SportTracks -@login_required() +@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) def workout_sporttracks_upload_view(request,id=0): message = "" # ready to upload. Hurray @@ -347,49 +337,46 @@ def workout_sporttracks_upload_view(request,id=0): return HttpResponseRedirect("/rowers/me/sporttracksauthorize/") - if (checkworkoutuser(request.user,w)): - data = sporttracksstuff.createsporttracksworkoutdata(w) - if not data: - message = "Data error" - messages.error(request,message) - url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), - }) - return HttpResponseRedirect(url) + data = sporttracksstuff.createsporttracksworkoutdata(w) - authorizationstring = str('Bearer ' + thetoken) - headers = {'Authorization': authorizationstring, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json'} + if not data: + message = "Data error" + messages.error(request,message) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':encoder.encode_hex(w.id), + }) + return HttpResponseRedirect(url) - url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json" - response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) + authorizationstring = str('Bearer ' + thetoken) + headers = {'Authorization': authorizationstring, + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json'} - - # check for duplicate error first - if (response.status_code == 409 ): - message = "Duplicate error" - messages.error(request,message) - w.uploadedtosporttracks = -1 - w.save() - elif (response.status_code == 201 or response.status_code==200): - s= response.json() - sporttracksid = sporttracksstuff.getidfromresponse(response) - w.uploadedtosporttracks = sporttracksid - w.save() - message = "Upload to SportTracks was successful" - messages.info(request,message) + url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json" + response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) - url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)}) - return HttpResponseRedirect(url) - else: - s = response - message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason - messages.error(request,message) + + # check for duplicate error first + if (response.status_code == 409 ): + message = "Duplicate error" + messages.error(request,message) + w.uploadedtosporttracks = -1 + w.save() + elif (response.status_code == 201 or response.status_code==200): + s= response.json() + sporttracksid = sporttracksstuff.getidfromresponse(response) + w.uploadedtosporttracks = sporttracksid + w.save() + message = "Upload to SportTracks was successful" + messages.info(request,message) + + url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)}) + return HttpResponseRedirect(url) else: - message = "You are not authorized to upload this workout" + s = response + message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason messages.error(request,message) url = reverse(r.defaultlandingpage, @@ -399,7 +386,7 @@ def workout_sporttracks_upload_view(request,id=0): return HttpResponseRedirect(url) -# Concept2 authorization +# Concept2 authorization @login_required() def rower_c2_authorize(request): # Generate a random string for the state parameter @@ -444,10 +431,10 @@ def rower_polar_authorize(request): # "scope":"accesslink.read_all" } url = "https://flow.polar.com/oauth2/authorization?" +urllib.parse.urlencode(params) - + return HttpResponseRedirect(url) - + # Runkeeper authorization @login_required() @@ -494,7 +481,7 @@ def rower_underarmour_authorize(request): state = str(uuid4()) redirect_uri = UNDERARMOUR_REDIRECT_URI - + url = 'https://www.mapmyfitness.com/v7.1/oauth2/authorize/?' \ 'client_id={0}&response_type=code&redirect_uri={1}'.format( UNDERARMOUR_CLIENT_KEY, redirect_uri @@ -647,7 +634,7 @@ def rower_process_callback(request): url = reverse('workouts_view') return HttpResponseRedirect(url) - + access_token = res[0] if access_token == 0: message = res[1] @@ -657,8 +644,8 @@ def rower_process_callback(request): url = reverse('workouts_view') return HttpResponseRedirect(url) - - + + expires_in = res[1] refresh_token = res[2] expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in) @@ -674,12 +661,12 @@ def rower_process_callback(request): messages.info(request,successmessage) url = reverse('workouts_view') - + return HttpResponseRedirect(url) -# dummy +# dummy @login_required() def rower_process_twittercallback(request): return "dummy" @@ -697,13 +684,13 @@ def rower_process_polarcallback(request): messages.error(request,message) url = reverse('workouts_view') - + return HttpResponseRedirect(url) access_token, expires_in, user_id = polarstuff.get_token(code) expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in) - + r = getrower(request.user) r.polartoken = access_token r.polartokenexpirydate = expirydatetime @@ -714,7 +701,7 @@ def rower_process_polarcallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) url = reverse('workouts_view') - + return HttpResponseRedirect(url) @@ -734,10 +721,10 @@ def rower_process_stravacallback(request): messages.error(request,message) url = reverse('workouts_view') - + return HttpResponseRedirect(url) - + res = stravastuff.get_token(code) if res[0]: @@ -762,7 +749,7 @@ def rower_process_stravacallback(request): message = "Something went wrong with the Strava authorization" messages.error(request,message) url = reverse('workouts_view') - + return HttpResponseRedirect(url) @@ -776,7 +763,7 @@ def rower_process_runkeepercallback(request): if access_token == 0: messages.error(request,"Something went wrong importing the token") url = reverse('workouts_view') - + return HttpResponseRedirect(url) @@ -789,7 +776,7 @@ def rower_process_runkeepercallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) url = reverse('workouts_view') - + return HttpResponseRedirect(url) @@ -806,7 +793,7 @@ def rower_process_sporttrackscallback(request): refresh_token = res[2] expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in) - + r = getrower(request.user) r.sporttrackstoken = access_token r.sporttrackstokenexpirydate = expirydatetime @@ -817,7 +804,7 @@ def rower_process_sporttrackscallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) url = reverse('workouts_view') - + return HttpResponseRedirect(url) @@ -833,7 +820,7 @@ def rower_process_underarmourcallback(request): expires_in = res[1] refresh_token = res[2] expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in) - + r = getrower(request.user) r.underarmourtoken = access_token r.underarmourtokenexpirydate = expirydatetime @@ -844,7 +831,7 @@ def rower_process_underarmourcallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) url = reverse('workouts_view') - + return HttpResponseRedirect(url) @@ -859,7 +846,7 @@ def rower_process_tpcallback(request): expires_in = res[1] refresh_token = res[2] expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in) - + r = getrower(request.user) r.tptoken = access_token r.tptokenexpirydate = expirydatetime @@ -870,7 +857,7 @@ def rower_process_tpcallback(request): successmessage = "Tokens stored. Good to go" messages.info(request,successmessage) url = reverse('workouts_view') - + return HttpResponseRedirect(url) @@ -892,7 +879,7 @@ def rower_process_testcallback(request): text += "\n\nRefresh Token:\n" text += refresh_token - + return HttpResponse(text) @@ -909,10 +896,10 @@ def workout_stravaimport_view(request,message="",userid=0): except NoTokenError: return HttpResponseRedirect("/rowers/me/stravaauthorize/") - + res = stravastuff.get_strava_workout_list(request.user) - + if (res.status_code != 200): if (res.status_code == 401): r = getrower(request.user) @@ -950,7 +937,7 @@ def workout_stravaimport_view(request,message="",userid=0): w.uploadedtostrava = int(stravaid) w.save() - + knownstravaids = uniqify([ w.uploadedtostrava for w in Workout.objects.filter(user=r) ]) @@ -985,7 +972,7 @@ def workout_stravaimport_view(request,message="",userid=0): r = getrower(request.user) - + return render(request,'strava_list_import.html', {'workouts':workouts, 'rower':r, @@ -1024,7 +1011,7 @@ def workout_runkeeperimport_view(request,message="",userid=0): r = item['type'] keys = ['id','distance','duration','starttime','type'] values = [i,d,ttot,s,r] - + res = dict(zip(keys,values)) workouts.append(res) @@ -1072,11 +1059,11 @@ def workout_underarmourimport_view(request,message="",userid=0): ttot = item['aggregates']['active_time_total'] except KeyError: ttot = 0 - + keys = ['id','distance','duration','starttime','type'] values = [i,d,ttot,s,r] thedict = dict(zip(keys,values)) - + workouts.append(thedict) rower = getrower(request.user) @@ -1090,7 +1077,7 @@ def workout_underarmourimport_view(request,message="",userid=0): 'name':'Concept2' }, ] - + return render(request,'underarmour_list_import.html', {'workouts':workouts, 'breadcrumbs':breadcrumbs, @@ -1115,13 +1102,13 @@ def workout_polarimport_view(request,userid=0): return HttpResponseRedirect(url) except: pass - + for exercise in exercises: try: d = exercise['distance'] except KeyError: d = 0 - + i = exercise['id'] transactionid = exercise['transaction-id'] starttime = exercise['start-time'] @@ -1156,13 +1143,13 @@ def workout_polarimport_view(request,userid=0): }) - + # The page where you select which SportTracks workout to import @login_required() def workout_sporttracksimport_view(request,message="",userid=0): - + res = sporttracksstuff.get_sporttracks_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): @@ -1215,7 +1202,7 @@ def workout_sporttracksimport_view(request,message="",userid=0): 'name':'SportTracks' }, ] - + return render(request,'sporttracks_list_import.html', {'workouts':workouts, 'breadcrumbs':breadcrumbs, @@ -1225,7 +1212,7 @@ def workout_sporttracksimport_view(request,message="",userid=0): }) return HttpResponse(res) - + # List of workouts on Concept2 logbook. This view only used for debugging @login_required() def c2listdebug_view(request,page=1,message=""): @@ -1262,7 +1249,7 @@ def c2listdebug_view(request,page=1,message=""): res = dict(zip(keys,values)) workouts.append(res) - + return render(request, 'c2_list_import2.html', {'workouts':workouts, @@ -1288,20 +1275,20 @@ def workout_getc2workout_all(request,page=1,message=""): alldata = {} for item in res.json()['data']: alldata[item['id']] = item - + knownc2ids = uniqify([ w.uploadedtoc2 for w in Workout.objects.filter(user=r) ]) newids = [c2id for c2id in c2ids if not c2id in knownc2ids] - + for c2id in newids: workoutid = c2stuff.create_async_workout(alldata, request.user,c2id) url = reverse('workouts_view') return HttpResponseRedirect(url) - - + + # List of workouts available on Concept2 logbook - for import @login_required() def workout_c2import_view(request,page=1,userid=0,message=""): @@ -1312,7 +1299,7 @@ def workout_c2import_view(request,page=1,userid=0,message=""): messages.info(request,"You cannot import other people's workouts from Concept2") r = getrower(request.user) - + try: thetoken = c2_open(request.user) except NoTokenError: @@ -1366,7 +1353,7 @@ def workout_c2import_view(request,page=1,userid=0,message=""): ] r = getrower(request.user) - + return render(request, 'c2_list_import2.html', {'workouts':workouts, @@ -1394,14 +1381,14 @@ def workout_getimportview(request,externalid,source = 'c2'): if not res[0]: messages.error(request,res[1]) url = reverse('workouts_view') - + return HttpResponseRedirect(url) strokedata = res[1] data = res[0] - + # Now works only for C2 try: if strokedata == 0: @@ -1433,16 +1420,16 @@ def workout_getimportview(request,externalid,source = 'c2'): if timezone_str is None: timezone_str = 'UTC' - + workoutdate = startdatetime.astimezone( pytz.timezone(timezone_str) ).strftime('%Y-%m-%d') starttime = startdatetime.astimezone( pytz.timezone(timezone_str) ).strftime('%H:%M:%S') - + r = getrower(request.user) - + id, message = dataprep.create_row_df(r, distance, duration, @@ -1463,7 +1450,7 @@ def workout_getimportview(request,externalid,source = 'c2'): }) return HttpResponseRedirect(url) - + # strokedata not empty - continue id,message = importsources[source].add_workout_from_data( request.user, @@ -1504,7 +1491,7 @@ def workout_getimportview(request,externalid,source = 'c2'): rowdata.write_csv(w.csvfilename,gzip=True) dataprep.update_strokedata(w.id,rowdata.df) - + if source == 'strava': w.uploadedtostrava = externalid @@ -1534,7 +1521,7 @@ def workout_getimportview(request,externalid,source = 'c2'): }) return HttpResponseRedirect(url) - + @@ -1599,7 +1586,7 @@ def workout_getstravaworkout_next(request): alldata = {} for item in res.json(): alldata[item['id']] = item - + knownstravaids = uniqify([ w.uploadedtostrava for w in Workout.objects.filter(user=r) ]) @@ -1608,10 +1595,8 @@ def workout_getstravaworkout_next(request): theid = newids[0] workoutid = stravastuff.create_async_workout(alldata,r.user,stravaid,debug=True) - - + + url = reverse('workouts_view') return HttpResponseRedirect(url) - - diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 05910fbc..aa158fcf 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1697,21 +1697,16 @@ def plannedsession_edit_view(request,id=0,userid=0): }) -@login_required() +@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) def plannedsession_detach_view(request,id=0,psid=0): r = getrequestrower(request) - try: - ps = PlannedSession.objects.get(id=psid) - except PlannedSession.DoesNotExist: - raise Http404("Planned Session does not exist") + + ps = get_object_or_404(PlannedSession,pk=psid) w = get_workout(id) - if (checkworkoutuser(request.user,w)==False): - return HttpResponseForbidden("Permission Error") - remove_workout_plannedsession(w,ps) url = reverse(plannedsession_view,kwargs={'id':psid}) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index fa81fc24..ac3867da 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -32,10 +32,17 @@ from icalendar import Calendar, Event from functools import reduce +from rules.contrib.views import PermissionRequiredMixin + import rowers.braintreestuff as braintreestuff import rowers.payments as payments from rowers.opaque import encoder +from rowers.rower_rules import ( + ispromember,is_coach_user,is_team_member,is_rower_team_member, + is_workout_user + ) + from django.shortcuts import render from django.template.loader import render_to_string @@ -98,7 +105,7 @@ from rowers.models import ( TrainingMesoCycleForm, TrainingMicroCycleForm, RaceLogo,RowerBillingAddressForm,PaidPlan, AlertEditForm, ConditionEditForm, - PlannedSessionComment,CoachRequest,CoachOffer,checkaccessplanuser, + PlannedSessionComment,CoachRequest,CoachOffer, VideoAnalysis ) from rowers.models import ( @@ -275,6 +282,11 @@ def getfavorites(r,row): return favorites,maxfav +def get_workout_by_opaqueid(request,id,**kwargs): + pk = encoder.decode_hex(id) + return get_object_or_404(Workout,pk=pk) + + def get_workout_default_page(request,id): if request.user.is_anonymous: return reverse('workout_view',kwargs={'id':id}) @@ -310,7 +322,7 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): except Rower.DoesNotExist: raise Http404("Rower doesn't exist") - if not checkaccessuser(request.user,r): + if userid != 0 and not is_coach_user(u,r): raise PermissionDenied("You have no access to this user") if notpermanent == False: @@ -343,7 +355,7 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False): except Rower.DoesNotExist: raise Http404("Rower doesn't exist") - if not checkaccessplanuser(request.user,r): + if not is_coach_user(request.user,r): raise PermissionDenied("You have no access to this user") if notpermanent == False: @@ -377,21 +389,6 @@ def get_workout(id): return w -def get_workout_permitted(user,id): - w = get_workout(id) - - if (checkworkoutuser(user,w)==False): - raise PermissionDenied("Access denied") - - return w - -def get_workout_permittedview(user,id): - w = get_workout(id) - - if (checkworkoutuserview(user,w)==False): - raise PermissionDenied("Access denied") - - return w def getvalue(data): perc = 0 @@ -774,9 +771,9 @@ def get_stored_tasks_status(request): return taskstatus -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def get_thumbnails(request,id): - row = get_workout_permitted(request.user,id) + row = get_workout_by_opaqueid(request,id) r = getrower(request.user) @@ -861,27 +858,6 @@ def get_blog_posts_old(request): -@login_required() -def get_testscript(request,id): - row = get_workout_permitted(request.user,id) - r = getrower(request.user) - - object = { - "script":""" -
- -
- """, - "div":""" -
-Hoi -
-""" - } - - - return JSONResponse([object,object]) - @login_required() def session_jobs_view(request): taskstatus = get_stored_tasks_status(request) @@ -990,10 +966,6 @@ from rowers.utils import ( import rowers.datautils as datautils -from rowers.models import ( - checkworkoutuser,checkaccessuser,checkviewworkouts,checkworkoutuserview - ) - # Check if a user is a Coach member def iscoachmember(user): if not user.is_anonymous: @@ -1044,21 +1016,7 @@ def hasplannedsessions(user): return result from rowers.utils import ProcessorCustomerError -from rowers.utils import isprorower -# Check if a user is a Pro member -def ispromember(user): - if user and not user.is_anonymous: - try: - r = Rower.objects.get(user=user) - except Rower.DoesNotExist: - r = Rower(user=user) - r.save() - - result = user.is_authenticated and isprorower(r) - else: - result = False - return result # More User/Rower utils def add_defaultfavorites(r): diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index f72537f9..36332560 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -64,8 +64,10 @@ def workout_video_view_mini(request,id=''): else: mode = 'erg' + + if request.user.is_authenticated: - mayedit = checkworkoutuser(request.user,w) and isprorower(request.user.rower) + mayedit = is_workout_user(request.user,w) and is_promember(request.user) rower = request.user.rower else: mayedit = False @@ -176,7 +178,7 @@ def workout_video_view(request,id=''): mode = 'erg' if request.user.is_authenticated: - mayedit = checkworkoutuser(request.user,w) and isprorower(request.user.rower) + mayedit = is_promember(request.user) and is_workout_user(request.user,w) rower = request.user.rower else: mayedit = False @@ -271,20 +273,18 @@ def workout_video_view(request,id=''): # Create a video compared with data +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember,login_url="/rowers/paidplans/", message="This functionality requires a Pro plan or higher", redirect_field_name=None) def workout_video_create_view(request,id=0): # get workout - w = get_workout_permitted(request.user,id) + w = get_workout_by_opaqueid(request,id) if w.workouttype in mytypes.otwtypes: mode = 'water' else: mode = 'erg' - - mayedit = checkworkoutuser(request.user,w) and isprorower(request.user.rower) - # get video ID and offset if request.method == 'POST': form = VideoAnalysisCreateForm(request.POST) @@ -457,12 +457,10 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False): }) # Switch from GPS to Impeller (only for SpeedCoach 2, if impeller data) -@login_required +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def otw_use_impeller(request,id=0): w = get_workout(id) - if (checkworkoutuser(request.user,w)==False): - raise PermissionDenied("Access denied") row = rdata(w.csvfilename) success = row.use_impellerdata() @@ -481,12 +479,10 @@ def otw_use_impeller(request,id=0): # Switch from Impeller to GPS (only for SpeedCoach 2, if impeller data) -@login_required +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def otw_use_gps(request,id=0): w = get_workout(id) - if (checkworkoutuser(request.user,w)==False): - raise PermissionDenied("Access denied") row = rdata(w.csvfilename) success = row.use_gpsdata() @@ -748,20 +744,13 @@ def fitness_metric_view(request,mode='rower',days=42): return HttpResponse("job queued") -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember,login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_update_cp_view(request,id=0): row = get_workout(id) - if (checkworkoutuser(request.user,row)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - - return HttpResponseRedirect(url) - row.rankingpiece = True row.save() @@ -778,17 +767,10 @@ def workout_update_cp_view(request,id=0): # Reload the workout and calculate the summary from the stroke data (lapIDx) -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_recalcsummary_view(request,id=0): row = get_workout(id) - if (checkworkoutuser(request.user,row)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - - return HttpResponseRedirect(url) - filename = row.csvfilename rowdata = rdata(filename) if rowdata: @@ -1359,7 +1341,7 @@ def team_comparison_select(request, if id: firstworkout = get_workout(id) - if not checkworkoutuserview(request.user,firstworkout): + if not is_workout_user(request.user,firstworkout): raise PermissionDenied("You are not allowed to use this workout") firstworkoutquery = Workout.objects.filter(id=encoder.decode_hex(id)) @@ -1791,7 +1773,7 @@ def workouts_view(request,message='',successmessage='', # check if access is allowed - if not checkviewworkouts(request.user,r): + if not is_rower_team_member(request.user,r): raise PermissionDenied("Access denied") @@ -2104,6 +2086,7 @@ def workout_fusion_list(request,id=0,message='',successmessage='', }) # Basic view of workout +@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_view(request,id=0): request.session['referer'] = absolute(request)['PATH'] @@ -2112,19 +2095,13 @@ def workout_view(request,id=0): else: rower = None - try: - row = Workout.objects.get(id=encoder.decode_hex(id)) - except Workout.DoesNotExist: - raise Http404("Workout doesn't exist") + row = get_workout_by_opaqueid(request,id) comments = WorkoutComment.objects.filter(workout=row) aantalcomments = len(comments) - if row.privacy == 'private' and not checkworkoutuser(request.user,row): - raise PermissionDenied("Access denied") - g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") for i in g: try: @@ -2204,6 +2181,7 @@ def workout_view(request,id=0): # Resets stroke data to raw data (pace) +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember,login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) @@ -2213,13 +2191,6 @@ def workout_undo_smoothenpace_view( row = get_workout(id) r = getrower(request.user) - if (checkworkoutuser(request.user,row)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - - return HttpResponseRedirect(url) - filename = row.csvfilename row = rdata(filename) if row == 0: @@ -2243,6 +2214,7 @@ def workout_undo_smoothenpace_view( # Data smoothing of pace data +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember,login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) @@ -2253,12 +2225,6 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): r = getrower(request.user) - if (checkworkoutuser(request.user,row)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - - return HttpResponseRedirect(url) filename = row.csvfilename row = rdata(filename) @@ -2373,6 +2339,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): 'id':row.id}) # Get weather for given location and date/time +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember,login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) @@ -2382,12 +2349,6 @@ def workout_downloadwind_view(request,id=0, row = get_workout(id) f1 = row.csvfilename - if (checkworkoutuser(request.user,row)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - - return HttpResponseRedirect(url) # create bearing rowdata = rdata(f1) @@ -2448,6 +2409,7 @@ def workout_downloadwind_view(request,id=0, return response # Get weather for given location and date/time +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) def workout_downloadmetar_view(request,id=0, airportcode=None, @@ -2455,12 +2417,7 @@ def workout_downloadmetar_view(request,id=0, row = get_workout(id) f1 = row.csvfilename - if (checkworkoutuser(request.user,row)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - return HttpResponseRedirect(url) # create bearing rowdata = rdata(f1) @@ -2523,6 +2480,7 @@ def workout_downloadmetar_view(request,id=0, # Show form to update wind data +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) def workout_wind_view(request,id=0,message="",successmessage=""): row = get_workout(id) @@ -2543,13 +2501,6 @@ def workout_wind_view(request,id=0,message="",successmessage=""): ] - if (checkworkoutuser(request.user,row)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - - return HttpResponseRedirect(url) - # get data f1 = row.csvfilename @@ -2659,17 +2610,13 @@ def workout_wind_view(request,id=0,message="",successmessage=""): # Show form to update River stream data (for river dwellers) +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) def workout_stream_view(request,id=0,message="",successmessage=""): row = get_workout(id) r = getrower(request.user) - if (checkworkoutuser(request.user,row)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - return HttpResponseRedirect(url) # create interactive plot @@ -2745,23 +2692,15 @@ def workout_stream_view(request,id=0,message="",successmessage=""): 'the_div':div}) # Form to set average crew weight and boat type, then run power calcs +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember, login_url="/rowers/paidplans",redirect_field_name=None) def workout_otwsetpower_view(request,id=0,message="",successmessage=""): w = get_workout(id) r = getrower(request.user) - mayedit = 0 - if request.user == w.user.user: - mayedit=1 - if checkworkoutuser(request.user,w): - mayedit=1 + mayedit = 1 - if (checkworkoutuser(request.user,w)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - return HttpResponseRedirect(url) if request.method == 'POST': @@ -2879,15 +2818,11 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): 'form':form, }) -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def instroke_view(request,id=0): w = get_workout(id) r = getrower(request.user) - mayedit = 0 - if request.user == w.user.user: - mayedit=1 - if checkworkoutuser(request.user,w): - mayedit=1 + mayedit = 1 breadcrumbs = [ { @@ -2909,12 +2844,7 @@ def instroke_view(request,id=0): g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime") # check if user is owner of this workout - if (checkworkoutuser(request.user,w)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - return HttpResponseRedirect(url) rowdata = rrdata(csvfile=w.csvfilename) try: @@ -2937,16 +2867,11 @@ def instroke_view(request,id=0): # generate instroke chart -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def instroke_chart(request,id=0,metric=''): w = get_workout(id) - if (checkworkoutuser(request.user,w)==False): - message = "You are not allowed to edit this workout" - messages.error(request,message) - url = reverse('workouts_view') - return HttpResponseRedirect(url) rowdata = rrdata(csvfile=w.csvfilename) instrokemetrics = rowdata.get_instroke_columns() @@ -2995,14 +2920,12 @@ def instroke_chart(request,id=0,metric=''): # data explorer -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_data_view(request, id=0): r = getrower(request.user) w = get_workout(id) - if not checkworkoutuser(request.user,w): - raise PermissionDenied('Access Denied') breadcrumbs = [ { @@ -3100,7 +3023,7 @@ def workout_data_view(request, id=0): # Stats page -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_stats_view(request,id=0,message="",successmessage=""): r = getrower(request.user) @@ -3109,7 +3032,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): mayedit = 0 if request.user == w.user.user: mayedit=1 - if checkworkoutuser(request.user,w): + if is_workout_user(request.user,w): mayedit=1 breadcrumbs = [ @@ -3135,8 +3058,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): # prepare data frame datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id)) - if (checkworkoutuserview(request.user,row)==False): - raise PermissionDenied('Access Denied') + datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly, ignoreadvanced=False) @@ -3377,11 +3299,12 @@ def workout_workflow_config2_view(request,userid=0): # Workflow View @login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_workflow_view(request,id): request.session['referer'] = absolute(request)['PATH'] request.session['lastworkout'] = id request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE - row = get_workout_permittedview(request.user,id) + row = get_workout_by_opaqueid(request,id) r = getrower(request.user) result = request.user.is_authenticated and ispromember(request.user) @@ -3463,7 +3386,7 @@ def workout_workflow_view(request,id): }) # The famous flex chart -@login_required() +@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_flexchart3_view(request,*args,**kwargs): try: @@ -3487,12 +3410,12 @@ def workout_flexchart3_view(request,*args,**kwargs): mayedit=0 if not request.user.is_anonymous: r = getrower(request.user) - result = request.user.is_authenticated and ispromember(request.user) + result = ispromember(request.user) if result: promember=1 if request.user == row.user.user: mayedit=1 - if checkworkoutuser(request.user,row): + if is_workout_user(request.user,row): mayedit=1 workouttype = 'ote' @@ -3977,7 +3900,7 @@ def workout_comment_view(request,id=0): # The basic edit page -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_edit_view(request,id=0,message="",successmessage=""): request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE request.session['referer'] = absolute(request)['PATH'] @@ -3986,8 +3909,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""): row = get_workout(id) - if (checkworkoutuser(request.user,row)==False): - raise PermissionDenied("Access denied") + if request.user.rower.rowerplan == 'basic' and 'speedcoach2' in row.workoutsource: data = getsmallrowdata_db(['wash'],ids=[encoder.decode_hex(id)]) @@ -4293,7 +4215,7 @@ def workout_map_view(request,id=0): # Image upload -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_uploadimage_view(request,id): is_ajax = False if request.is_ajax(): @@ -4319,8 +4241,7 @@ def workout_uploadimage_view(request,id): ] - if not checkworkoutuser(request.user,w): - raise PermissionDenied("You are not allowed to edit this workout") + images = GraphImage.objects.filter(workout=w) @@ -4398,7 +4319,7 @@ def workout_uploadimage_view(request,id): # Generic chart creation -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_add_chart_view(request,id,plotnr=1): w = get_workout(id) @@ -4406,26 +4327,24 @@ def workout_add_chart_view(request,id,plotnr=1): plotnr = int(plotnr) - if (checkworkoutuser(request.user,w)==False): - raise PermissionDenied("You are not allowed add plots to this workout") + + f1 = w.csvfilename[6:-4] + timestr = strftime("%Y%m%d-%H%M%S") + imagename = f1+timestr+'.png' + u = w.user.user + r = getrower(u) + title = w.name + res,jobid = uploads.make_plot( + r,w,f1,w.csvfilename,'timeplot',title,plotnr=plotnr, + imagename=imagename + ) + if res == 0: + messages.error(request,jobid) else: - f1 = w.csvfilename[6:-4] - timestr = strftime("%Y%m%d-%H%M%S") - imagename = f1+timestr+'.png' - u = w.user.user - r = getrower(u) - title = w.name - res,jobid = uploads.make_plot( - r,w,f1,w.csvfilename,'timeplot',title,plotnr=plotnr, - imagename=imagename - ) - if res == 0: - messages.error(request,jobid) - else: - try: - request.session['async_tasks'] += [(jobid,'make_plot')] - except KeyError: - request.session['async_tasks'] = [(jobid,'make_plot')] + try: + request.session['async_tasks'] += [(jobid,'make_plot')] + except KeyError: + request.session['async_tasks'] = [(jobid,'make_plot')] url = reverse(r.defaultlandingpage,kwargs={'id':encoder.encode_hex(w.id)}) @@ -4437,12 +4356,13 @@ def workout_add_chart_view(request,id,plotnr=1): @login_required +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_toggle_ranking(request,id=0): is_ajax = False if request.is_ajax(): is_ajax = True - row = get_workout_permitted(request.user,id) + row = get_workout_by_opaqueid(request,id) row.rankingpiece = not row.rankingpiece row.save() @@ -5237,9 +5157,9 @@ def graph_show_view(request,id): raise Http404("This workout doesn't exist") # Restore original stroke data and summary -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_summary_restore_view(request,id,message="",successmessage=""): - row = get_workout_permitted(request.user,id) + row = get_workout_by_opaqueid(request,id) s = "" # still here - this is a workout we may edit @@ -5287,11 +5207,12 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): return HttpResponseRedirect(url) # Split a workout +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) @user_passes_test(ispromember,login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_split_view(request,id=0): - row = get_workout_permitted(request.user,id) + row = get_workout_by_opaqueid(request,id) r = row.user @@ -5398,8 +5319,8 @@ def workout_fusion_view(request,id1=0,id2=1): w1 = Workout.objects.get(id=id1) w2 = Workout.objects.get(id=id2) r = w1.user - if (checkworkoutuser(request.user,w1)==False) or \ - (checkworkoutuser(request.user,w2)==False): + if (is_workout_user(request.user,w1)==False) or \ + (is_workout_user(request.user,w2)==False): raise PermissionDenied("You are not allowed to use these workouts") except Workout.DoesNotExist: raise Http404("One of the workouts doesn't exist") @@ -5475,10 +5396,10 @@ def workout_fusion_view(request,id1=0,id2=1): # Edit the splits/summary -@login_required() +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_summary_edit_view(request,id,message="",successmessage="" ): - row = get_workout_permitted(request.user,id) + row = get_workout_by_opaqueid(request,id) r = getrower(request.user) breadcrumbs = [ { @@ -6024,10 +5945,11 @@ def workout_code_delete_view(request,id=0): return HttpResponseRedirect(url) -class WorkoutDelete(DeleteView): +class WorkoutDelete(PermissionRequiredMixin,DeleteView): login_required = True model = Workout template_name = 'workout_delete_confirm.html' + permission_required = 'workout.change_workout' # extra parameters def get_context_data(self, **kwargs): @@ -6082,7 +6004,6 @@ class WorkoutDelete(DeleteView): except Workout.DoesNotExist: raise Http404("One of the workouts doesn't exist") # obj = super(WorkoutDelete, self).get_object(*args, **kwargs) - if not checkaccessuser(self.request.user,obj.user): - raise PermissionDenied('You are not allowed to delete this workout') + return obj From 9cbeb3fc2d85c5d9be00bf8f059514befe2c2019 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 12 Jan 2020 18:06:23 +0100 Subject: [PATCH 10/80] removed checkaccessuers from planviews not checked --- rowers/views/planviews.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index aa158fcf..1b04f7ed 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1959,7 +1959,7 @@ class PlannedSessionDelete(DeleteView): 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 is_coach_user(m,self.request.user.rower): raise PermissionDenied('You are not allowed to delete this planned session') return obj @@ -2103,7 +2103,7 @@ def rower_delete_trainingtarget(request,id=0): except TrainingPlan.DoesNotExist: raise Http404("Training Plan Does Not Exist") - if checkaccessuser(request.user,target.manager): + if is_coach_user(target.manager,request.user.rower): target.delete() messages.info(request,"We have deleted the training target") else: @@ -2123,7 +2123,7 @@ def rower_delete_trainingplan(request,id=0): except TrainingPlan.DoesNotExist: raise Http404("Training Plan Does Not Exist") - if checkaccessuser(request.user,plan.manager): + if is_coach_user(plan.manager.user,request.user.rower): plan.delete() messages.info(request,"We have deleted the training plan") else: @@ -2140,7 +2140,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 is_coach_user(obj.manager.user,self.request.user): raise PermissionDenied('You are not allowed to delete this training plan') return obj @@ -2206,7 +2206,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 is_coach_user(obj.plan.plan.plan.manager.user,self.request.user): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2268,7 +2268,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 is_coach_user(obj.plan.plan.manager.user,self.request.user): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2322,7 +2322,7 @@ 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 is_coach_user(obj.plan.manager.user,self.request.user): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2345,7 +2345,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(plan.manager.user,request.user): if request.user.rower not in plan.rowers.all(): raise PermissionDenied("Access denied") @@ -2439,7 +2439,7 @@ def rower_trainingplan_view(request, r = getrequestrower(request,userid=userid) - if not checkaccessuser(request.user,plan.manager): + if not is_coach_user(plan.manager.user,request.user): if request.user.rower not in plan.rowers.all(): raise PermissionDenied("Access denied") @@ -2568,7 +2568,7 @@ class TrainingMacroCycleUpdate(UpdateView): 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 is_coach_user(plan.manager.user,self.request.user): raise PermissionDenied('You are not allowed to edit this training plan cycle') else: obj.type = 'userdefined' @@ -2850,7 +2850,7 @@ def planmesocyclebyweek(request,id=0,userid=0): except TrainingMesoCycle.DoesNotExist: raise Http404("Training Cycle does not exist") - if not checkaccessuser(request.user,cycle.plan.plan.manager): + if not is_coach_user(cycle.plan.plan.manager.user,request.user): raise PermissionDenied("You are not allowed to do this") micros = TrainingMicroCycle.objects.filter(plan=cycle) @@ -2905,7 +2905,7 @@ def planmacrocyclebymonth(request,id=0,userid=0): except TrainingMacroCycle.DoesNotExist: raise Http404("Training Cycle does not exist") - if not checkaccessuser(request.user,cycle.plan.manager): + if not is_coach_user(cycle.plan.manager.user,request.user): raise PermissionDenied("You are not allowed to do this") mesos = TrainingMesoCycle.objects.filter(plan=cycle) From 09ae7cbe770c1d45d35054bbe21a0b3cd8cecdef Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 12 Jan 2020 22:10:20 +0100 Subject: [PATCH 11/80] passing tests - user / workouts permissions done --- rowers/rower_rules.py | 34 ++++++++-------------------------- rowers/tests/test_misc.py | 4 ++-- rowers/views/exportviews.py | 6 +++--- rowers/views/importviews.py | 14 +++++++------- rowers/views/planviews.py | 22 +++++++++++----------- rowers/views/statements.py | 7 ++++--- rowers/views/workoutviews.py | 9 +++++---- 7 files changed, 40 insertions(+), 56 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 258c86b3..1ad9d6f0 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -51,6 +51,7 @@ ispromember = is_promember | is_protrial def can_plan(user): return user.rower.rowerplan in ['plan','coach','freecoach'] +# checks if rower is coach of user @rules.predicate def is_coach_user(user,rower): try: @@ -67,7 +68,7 @@ def is_coach_user(user,rower): newcoaches = group.get_coaches() for coach in newcoaches: coaches.append(coach) - print(coaches) + for coach in coaches: if rower == coach: return True @@ -126,36 +127,16 @@ def is_workout_user(user,workout): if workout.user == r: return True - coaches = [] - for group in workout.user.coachinggroups.all(): - coach = group.coachingrole - coaches.append(coach) - for coach in coaches: - if r == coach and workout.privacy == 'visible': - return True - - return False + return is_coach_user(workout.user.user,user.rower) + @rules.predicate def can_view_workout(user,workout): - if user.is_anonymous: - if workout.privacy != 'private': - return True - return False - - try: - r = user.rower - except AttributeError: - return False - - teams = workout.user.team.all() - - for team in teams: - if team in r.team.all(): - return True - + if workout.privacy != 'private': + return True return False + rules.add_perm('workout.change_workout',is_workout_user) # replaces checkworkoutuser rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutuserview @@ -166,6 +147,7 @@ rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutu # PLANNING permissions # checkaccessplanuser (models.py) +# getrequestrower, getrequestplanrower # TEAM permissions diff --git a/rowers/tests/test_misc.py b/rowers/tests/test_misc.py index cf97672c..e2174bdf 100644 --- a/rowers/tests/test_misc.py +++ b/rowers/tests/test_misc.py @@ -8,7 +8,7 @@ from .statements import * nu = datetime.datetime.now() - +from rowers.rower_rules import is_workout_user #@pytest.mark.django_db class WorkoutTests(TestCase): @@ -28,7 +28,7 @@ class WorkoutTests(TestCase): duration="0:55:00",distance=8000) def test_checkworkoutuser(self): - self.assertEqual(checkworkoutuser(self.u,self.w),True) + self.assertEqual(is_workout_user(self.u,self.w),True) #@pytest.mark.django_db class C2Tests(TestCase): diff --git a/rowers/views/exportviews.py b/rowers/views/exportviews.py index 08f2e064..afdfc7c9 100644 --- a/rowers/views/exportviews.py +++ b/rowers/views/exportviews.py @@ -6,7 +6,7 @@ from rowers.views.statements import * # Export workout to TCX and send to user's email address -@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) +@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) @@ -168,7 +168,7 @@ def course_kmldownload_view(request,id=0): # Export workout to GPX and send to user's email address -@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) +@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) @@ -234,7 +234,7 @@ def workouts_summaries_email_view(request): # Get Workout CSV file and send it to user's email address -@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_csvemail_view(request,id=0): r = getrower(request.user) diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index 7b77b68b..a823c788 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -13,7 +13,7 @@ def default(o): # Send workout to TP -@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) def workout_tp_upload_view(request,id=0): message = "" @@ -25,7 +25,7 @@ def workout_tp_upload_view(request,id=0): return HttpResponseRedirect("/rowers/me/tpauthorize/") # ready to upload. Hurray - w = get_object_or_404(Workout,pk=id) + w = get_workout_by_opaqueid(request,id) r = w.user @@ -66,7 +66,7 @@ def workout_tp_upload_view(request,id=0): # Send workout to Strava # abundance of error logging here because there were/are some bugs -@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) +@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) @@ -202,7 +202,7 @@ def workout_c2_upload_view(request,id=0): return response # Upload workout to RunKeeper -@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) +@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) @@ -263,7 +263,7 @@ def workout_runkeeper_upload_view(request,id=0): return HttpResponseRedirect(url) # Upload workout to Underarmour -@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) +@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) @@ -324,7 +324,7 @@ def workout_underarmour_upload_view(request,id=0): return HttpResponseRedirect(url) # Upload workout to SportTracks -@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) +@permission_required('workout.change_workout',fn=get_workout_by_opaqueid) def workout_sporttracks_upload_view(request,id=0): message = "" # ready to upload. Hurray @@ -378,7 +378,7 @@ def workout_sporttracks_upload_view(request,id=0): 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), diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 1b04f7ed..ae16fb94 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1697,7 +1697,7 @@ def plannedsession_edit_view(request,id=0,userid=0): }) -@permission_required('workout.change_workout',fn=objectgetter(Workout, 'id')) +@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) @@ -1959,7 +1959,7 @@ class PlannedSessionDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(PlannedSessionDelete, self).get_object(*args, **kwargs) m = Rower.objects.get(user=obj.manager) - if not is_coach_user(m,self.request.user.rower): + if not is_coach_user(m.user,self.request.user.rower): raise PermissionDenied('You are not allowed to delete this planned session') return obj @@ -2140,7 +2140,7 @@ class TrainingPlanDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(TrainingPlanDelete, self).get_object(*args, **kwargs) - if not is_coach_user(obj.manager.user,self.request.user): + if not is_coach_user(obj.manager.user,self.request.user.rower): raise PermissionDenied('You are not allowed to delete this training plan') return obj @@ -2206,7 +2206,7 @@ class MicroCycleDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(MicroCycleDelete, self).get_object(*args, **kwargs) - if not is_coach_user(obj.plan.plan.plan.manager.user,self.request.user): + if not is_coach_user(obj.plan.plan.plan.manager.user,self.request.user.rower): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2268,7 +2268,7 @@ class MesoCycleDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(MesoCycleDelete, self).get_object(*args, **kwargs) - if not is_coach_user(obj.plan.plan.manager.user,self.request.user): + if not is_coach_user(obj.plan.plan.manager.user,self.request.user.rower): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2322,7 +2322,7 @@ class MacroCycleDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(MacroCycleDelete, self).get_object(*args, **kwargs) - if not is_coach_user(obj.plan.manager.user,self.request.user): + if not is_coach_user(obj.plan.manager.user,self.request.user.rower): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2345,7 +2345,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 is_coach_user(plan.manager.user,request.user): + if not is_coach_user(plan.manager.user,request.user.rower): if request.user.rower not in plan.rowers.all(): raise PermissionDenied("Access denied") @@ -2439,7 +2439,7 @@ def rower_trainingplan_view(request, r = getrequestrower(request,userid=userid) - if not is_coach_user(plan.manager.user,request.user): + if not is_coach_user(plan.manager.user,request.user.rower): if request.user.rower not in plan.rowers.all(): raise PermissionDenied("Access denied") @@ -2568,7 +2568,7 @@ class TrainingMacroCycleUpdate(UpdateView): 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 is_coach_user(plan.manager.user,self.request.user): + if not is_coach_user(obj.plan.manager.user,self.request.user.rower): raise PermissionDenied('You are not allowed to edit this training plan cycle') else: obj.type = 'userdefined' @@ -2850,7 +2850,7 @@ def planmesocyclebyweek(request,id=0,userid=0): except TrainingMesoCycle.DoesNotExist: raise Http404("Training Cycle does not exist") - if not is_coach_user(cycle.plan.plan.manager.user,request.user): + if not is_coach_user(cycle.plan.plan.manager.user,request.user.rower): raise PermissionDenied("You are not allowed to do this") micros = TrainingMicroCycle.objects.filter(plan=cycle) @@ -2905,7 +2905,7 @@ def planmacrocyclebymonth(request,id=0,userid=0): except TrainingMacroCycle.DoesNotExist: raise Http404("Training Cycle does not exist") - if not is_coach_user(cycle.plan.manager.user,request.user): + if not is_coach_user(cycle.plan.manager.user,request.user.rower): raise PermissionDenied("You are not allowed to do this") mesos = TrainingMesoCycle.objects.filter(plan=cycle) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index ac3867da..b4d4bbf9 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -313,16 +313,18 @@ 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) else: r = getrower(request.user) + u = r.user except Rower.DoesNotExist: raise Http404("Rower doesn't exist") - if userid != 0 and not is_coach_user(u,r): + if userid != 0 and not is_coach_user(u,request.user.rower): raise PermissionDenied("You have no access to this user") if notpermanent == False: @@ -355,7 +357,7 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False): except Rower.DoesNotExist: raise Http404("Rower doesn't exist") - if not is_coach_user(request.user,r): + if not is_coach_user(r.user,request.user.rower): raise PermissionDenied("You have no access to this user") if notpermanent == False: @@ -430,7 +432,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) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 36332560..4d70bfa3 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -3023,7 +3023,7 @@ def workout_data_view(request, id=0): # Stats page -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) +@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) @@ -3299,7 +3299,7 @@ def workout_workflow_config2_view(request,userid=0): # Workflow View @login_required() -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) +@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 @@ -5876,7 +5876,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(obj.workout.user,self.request.user): raise PermissionDenied('You are not allowed to delete this analysis') return obj @@ -5929,7 +5929,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 From af609dd7e022bd2c03838818e0f775a4b24946b3 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 12 Jan 2020 22:20:47 +0100 Subject: [PATCH 12/80] fixed obsolete usage of rowingdata(csvfile) --- rowers/dataprepnodjango.py | 8 ++++---- rowers/mailprocessing.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index f41498fb..15f66293 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -140,10 +140,10 @@ def timedeltaconv(x): def rdata(file,rower=rrower()): try: - res = rrdata(file,rower=rower) + res = rrdata(csvfile=file,rower=rower) except IOError: try: - res = rrdata(file+'.gz',rower=rower) + res = rrdata(csvfile=file+'.gz',rower=rower) except IOError: res = 0 @@ -948,7 +948,7 @@ def smalldataprep(therows,xparam,yparam1,yparam2): f1 = workout.csvfilename try: - rowdata = dataprep(rrdata(f1).df) + rowdata = dataprep(rrdata(csvfile=f1).df) rowdata = pd.DataFrame({xparam: rowdata[xparam], yparam1: rowdata[yparam1], @@ -960,7 +960,7 @@ def smalldataprep(therows,xparam,yparam1,yparam2): df = pd.concat([df,rowdata],ignore_index=True) except IOError: try: - rowdata = dataprep(rrdata(f1+'.gz').df) + rowdata = dataprep(rrdata(csvfile=f1+'.gz').df) rowdata = pd.DataFrame({xparam: rowdata[xparam], yparam1: rowdata[yparam1], yparam2: rowdata[yparam2], diff --git a/rowers/mailprocessing.py b/rowers/mailprocessing.py index 37017602..fb275a11 100644 --- a/rowers/mailprocessing.py +++ b/rowers/mailprocessing.py @@ -64,18 +64,18 @@ def send_confirm(user, name, link, options): def rdata(file, rower=rrower()): """ Reads rowingdata data or returns 0 on Error """ try: - result = rrdata(file, rower=rower) + result = rrdata(csvfile=file, rower=rower) except IOError: try: - result = rrdata(file + '.gz', rower=rower) + result = rrdata(csvfile=file + '.gz', rower=rower) except IOError: result = 0 except TypeError: try: - result = rrdata(file) + result = rrdata(csvfile=file) except IOError: try: - result = rrdata(file+'.gz', rower=rower) + result = rrdata(csvfile=file+'.gz', rower=rower) except IOError: result = 0 From bf1ea191e780952e3807492b46eeddaecd65d438 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 13 Jan 2020 15:33:50 +0100 Subject: [PATCH 13/80] added annotations to rules on rules to add --- rowers/rower_rules.py | 108 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 1ad9d6f0..f55cbc61 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -5,6 +5,30 @@ import datetime # USER permissions +""" +USER permissions +- There are 5 types of user: basic, pro, plan, coach, freecoach +- These methods exist on user + - user.rower + - user.is_anonymous + - user.is_staff + +- These methods exist on rower + - rower.rowerplan + - rower.protrialexpires + - rower.plantrialexpires + - rower.mycoachgroup + - rower.coachinggroups + - rower.team + +- These Rules apply to Rowers + - Free Coach user cannot have workouts + - Coach can edit athlete settings (if in coachinggroup) + - Coach can run analytics for athlete + - Pro and Plan user cannot run analysis for members of their groups + +""" + @rules.predicate def user_is_not_basic(user): if user.rower.rowerplan != 'basic': @@ -114,6 +138,25 @@ rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser # WORKOUT permissions +""" +WORKOUT permissions +- These methods exist on Workout + - workout.privacy + +- These rules apply to workouts + - User can add, delete and change their own workouts + - Coach can add and change workouts for their athletes, but not delete + - Pro and Plan user cannot add or change an athlete's workout + - Pro, Basic users can view team members' workout + - Anonymous users can view team members' workout (but not see list of workouts) + + - Rules for Workouts are transferred to objects related to the Workout, + - Charts + - Video Analysis + - WorkoutComment? + +""" + @rules.predicate def is_workout_user(user,workout): if user.is_anonymous: @@ -128,7 +171,7 @@ def is_workout_user(user,workout): return True return is_coach_user(workout.user.user,user.rower) - + @rules.predicate def can_view_workout(user,workout): @@ -146,10 +189,52 @@ rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutu # PLANNING permissions +""" +- These rules apply to planning + - Free coach can create planned sessions and team planned sessions + - Self coach and higher can create planned sessions and team planned sessions + - Coach can create planned sessions and team planned sessions + - Self Coach and higher can create planned sessions and team planned sessions + - Pro or Basic cannot create planned sessions or team planned sessions + - WHO can comment (plannedsession_comment_view) + - Only Session manager can change session + - Strict View rules (stricter than workouts) + + - TrainingTarget + - rules for view, add, change, delete + - TrainingPlan + - rules for view, add, change, delete + - Cycle + - inherits rules from TrainingPlan + - PlannedSession + - rules for view, add, change, delete, clone, save as template + - check team + + - Special rules for Race (cannot be edited ex post) + +""" + # checkaccessplanuser (models.py) # getrequestrower, getrequestplanrower -# TEAM permissions +# TEAM (group) permissions + +""" +- These methods exist on team + - team.manager + - team.private + +- These rules apply to a team + - A Pro rower can be manager of only 1 team (as manager) + - A coach can have any number of teams (as manager) + - Basic user cannot manage a group + - Basic user cannot join groups led by Free Coach or Pro + - Basic athletes can be member of Coach led group + - Plan user can create 1 group (as manager) and not more + - Pro users (and higher) can join team led by other Pro (or higher) user + - On downgrade, Coach users lose all but their oldest team (add test!) + +""" @rules.predicate def is_team_manager(user,team): @@ -177,3 +262,22 @@ rules.add_perm('teams.view_team',can_view_team) rules.add_perm('teams.add_team',user_is_not_basic) rules.add_perm('teams.change_team',is_team_manager) rules.add_perm('teams.delete_team',is_team_manager) + +# RACING permissions + +""" + - VirtualRace + - rules to add, view, delete, change + - GeoCourse + - rules to add, view, delete, change +""" + + +# ANALYSIS permissions + +""" + - Conditions + - Alerts + - rules to add, view, delete, change + - cpdata +""" From d6c492080b72c06f3f92e8974158f708eb3eb4df Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 13 Jan 2020 16:20:50 +0100 Subject: [PATCH 14/80] added is_coach permissions to user views (not tested) --- rowers/rower_rules.py | 36 ++++++++++++++++++++++++++-- rowers/views/statements.py | 3 +++ rowers/views/userviews.py | 49 ++++++++++---------------------------- 3 files changed, 49 insertions(+), 39 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index f55cbc61..dad9d33d 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -43,6 +43,15 @@ def user_is_not_basic(user): def is_coach(user): return user.rower.rowerplan in ['coach','freecoach'] +@rules.predicate +def is_planmember(user): + try: + r = user.rower + except AttributeError: + return False + + return r.rowerplan in ['coach','plan'] # freecoach? + @rules.predicate def is_promember(user): try: @@ -50,7 +59,7 @@ def is_promember(user): except AttributeError: return False - return r.rowerplan in ['pro','coach','plan'] + return r.rowerplan in ['pro','coach','plan'] # freecoach? @rules.predicate def is_protrial(user): @@ -69,6 +78,23 @@ def is_protrial(user): ispromember = is_promember | is_protrial +@rules.predicate +def is_plan(user): + try: + r = user.rower + except AttributeError: + return False + + if r.rowerplan == 'basic': + return r.plantrialexpires >= datetime.date.today() + if r.rowerplan == 'freecoach': + if r.mycoachgroup is not None: + return len(r.mycoachgroup)>=4 + + return False + +isplanmember = is_planmember | is_plantrial + # User / Coach relationships (Rower object) @rules.predicate @@ -99,6 +125,7 @@ def is_coach_user(user,rower): return False +# check if rower and user are members of the same team @rules.predicate def is_rower_team_member(user,rower): if user.rower == rower: @@ -115,6 +142,7 @@ def is_rower_team_member(user,rower): return False +# check if user can plan for the rower @rules.predicate def can_plan_user(user,rower): try: @@ -157,6 +185,7 @@ WORKOUT permissions """ +# check if user is owner or coach of owner of workout @rules.predicate def is_workout_user(user,workout): if user.is_anonymous: @@ -172,7 +201,7 @@ def is_workout_user(user,workout): return is_coach_user(workout.user.user,user.rower) - +# check if user can see workout @rules.predicate def can_view_workout(user,workout): if workout.privacy != 'private': @@ -236,15 +265,18 @@ rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutu """ +# check if user is manager of the team @rules.predicate def is_team_manager(user,team): return team.manager == user +# check is user is member of team @rules.predicate def is_team_member(user,team): members = team.rower.all() return user in [member.user for member in members] +# check if user can view team @rules.predicate def can_view_team(user,team): # user based diff --git a/rowers/views/statements.py b/rowers/views/statements.py index b4d4bbf9..b3820bca 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -297,6 +297,9 @@ def get_workout_default_page(request,id): else: return reverse('workout_workflow_view',kwargs={'id':id}) +def get_user_by_id(request,id): + return get_object_or_404(User,pk=id) + def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): userid = int(userid) diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index 205104fe..b9e89509 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -5,31 +5,6 @@ from __future__ import unicode_literals from rowers.views.statements import * -@login_required() -def survey(request): - - r = getrower(request.user) - - surveyform = SurveyForm() - - if request.method == 'POST': - form = SurveyForm(request.POST) - r.surveydone = True - r.surveydonedate = timezone.now() - r.save() - - nexturl = request.GET.get('next') - return HttpResponseRedirect(nexturl) - - context = { - 'teams':get_my_teams(request.user), - 'rower':r, - 'form':surveyform, - } - - - return render(request,'survey.html',context) - @login_required() def start_trial_view(request): r = getrower(request.user) @@ -100,11 +75,11 @@ def start_plantrial_view(request): return HttpResponseRedirect(url) # Page where user can manage his favorite charts -@login_required() -def rower_favoritecharts_view(request,userid=0): +@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) +def rower_favoritecharts_view(request,id=0): message = '' successmessage = '' - r = getrequestrower(request,userid=userid,notpermanent=True) + r = getrequestrower(request,userid=id,notpermanent=True) favorites = FavoriteChart.objects.filter(user=r).order_by('id') aantal = len(favorites) favorites_data = [{'yparam1':f.yparam1, @@ -167,9 +142,9 @@ def rower_favoritecharts_view(request,userid=0): return render(request,'favoritecharts.html',context) # page where user sets his export settings -@login_required() -def rower_exportsettings_view(request,userid=0): - r = getrequestrower(request,userid=userid) +@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) +def rower_exportsettings_view(request,id=0): + r = getrequestrower(request,userid=id) if request.method == 'POST': form = RowerExportForm(request.POST) if form.is_valid(): @@ -203,9 +178,9 @@ 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() -def rower_edit_view(request,rowerid=0,userid=0,message=""): - r = getrequestrower(request,rowerid=rowerid,userid=userid,notpermanent=True) +@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) +def rower_edit_view(request,rowerid=0,id=0,message=""): + r = getrequestrower(request,rowerid=rowerid,userid=id,notpermanent=True) rowerid = r.id @@ -298,9 +273,9 @@ 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() -def rower_prefs_view(request,userid=0,message=""): - r = getrequestrower(request,userid=userid,notpermanent=True) +@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) +def rower_prefs_view(request,id=0,message=""): + r = getrequestrower(request,userid=id,notpermanent=True) rowerid = r.id From 8de759425d8f0672196b07f220a901efdb33fbac Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 13 Jan 2020 16:26:35 +0100 Subject: [PATCH 15/80] Adding tests for all 4 settings pages --- rowers/tests/test_permissions.py | 77 +++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index bd8cc51d..d2ddc96a 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -644,7 +644,8 @@ class PermissionsViewTests(TestCase): ## Coach can edit on behalf of athlete if permitted - def test_coach_edit_athlete_settings(self): + # 1, Preferences + def test_coach_edit_athlete_prefs(self): self.rbasic.team.add(self.teamcoach) self.rbasic.coachinggroups.add(self.coachinggroup) @@ -656,7 +657,7 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) self.assertEqual(response.status_code,200) - def test_coach_edit_athlete_settings_not(self): + def test_coach_edit_athlete_prefs_not(self): self.rbasic.team.add(self.teamcoach) login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) @@ -667,6 +668,78 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) self.assertEqual(response.status_code,403) + # 2, Favorite Charts + def test_coach_edit_athlete_settings(self): + self.rbasic.team.add(self.teamcoach) + self.rbasic.coachinggroups.add(self.coachinggroup) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('rower_favoritecharts_view',kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + def test_coach_edit_athlete_settings_not(self): + self.rbasic.team.add(self.teamcoach) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('rower_favoritecharts_view',kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,403) + + # 3, Export Settings + def test_coach_edit_athlete_settings(self): + self.rbasic.team.add(self.teamcoach) + self.rbasic.coachinggroups.add(self.coachinggroup) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('rower_exportsettings_view',kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + def test_coach_edit_athlete_settings_not(self): + self.rbasic.team.add(self.teamcoach) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('rower_exportsettings_view',kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,403) + + # 4, Account settings + def test_coach_edit_athlete_settings(self): + self.rbasic.team.add(self.teamcoach) + self.rbasic.coachinggroups.add(self.coachinggroup) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('rower_edit_view',kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + def test_coach_edit_athlete_settings_not(self): + self.rbasic.team.add(self.teamcoach) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('rower_edit_view',kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,403) + ## Coach can run analytics for athlete @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats) def test_coach_edit_athlete_analysis(self,mocked_df): From 926ff6d5e5d62eeec0b0d25c1ed1beccc13c1457 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 13 Jan 2020 16:42:02 +0100 Subject: [PATCH 16/80] fixes to pass tests --- rowers/rower_rules.py | 2 +- rowers/tests/test_permissions.py | 20 ++++++++++---------- rowers/urls.py | 8 ++++---- rowsandall_app/urls.py | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index dad9d33d..bac7b6ed 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -79,7 +79,7 @@ def is_protrial(user): ispromember = is_promember | is_protrial @rules.predicate -def is_plan(user): +def is_plantrial(user): try: r = user.rower except AttributeError: diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index d2ddc96a..ad2d745e 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -652,7 +652,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_prefs_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) @@ -663,7 +663,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_prefs_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -676,7 +676,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_favoritecharts_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_favoritecharts_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) @@ -687,7 +687,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_favoritecharts_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_favoritecharts_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -700,7 +700,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_exportsettings_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_exportsettings_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) @@ -711,7 +711,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_exportsettings_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_exportsettings_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -724,7 +724,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_edit_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_edit_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) @@ -735,7 +735,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_edit_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_edit_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -981,7 +981,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.uplan2.username, password=self.uplan2password) self.assertTrue(login) - url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_prefs_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -1103,7 +1103,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.upro2.username, password=self.upro2password) self.assertTrue(login) - url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) + url = reverse('rower_prefs_view',kwargs={'id':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) diff --git a/rowers/urls.py b/rowers/urls.py index 15b7768f..de413d87 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -447,7 +447,7 @@ urlpatterns = [ re_path(r'^user-multiflex-data/$',views.multiflex_data,name='multiflex_data'), re_path(r'^me/deactivate/$',views.deactivate_user,name='deactivate_user'), re_path(r'^me/delete/$',views.remove_user,name='remove_user'), - re_path(r'^survey/$',views.survey,name='survey'), +# re_path(r'^survey/$',views.survey,name='survey'), re_path(r'^me/gdpr-optin-confirm/?/$',views.user_gdpr_confirm,name='user_gdpr_confirm'), re_path(r'^me/gdpr-optin-confirm/$',views.user_gdpr_confirm,name='user_gdpr_confirm'), re_path(r'^me/gdpr-optin/?/$',views.user_gdpr_optin,name='user_gdpr_optin'), @@ -455,7 +455,7 @@ urlpatterns = [ re_path(r'^me/teams/$',views.rower_teams_view,name='rower_teams_view'), re_path(r'^me/calcdps/$',views.rower_calcdps_view,name='rower_calcdps_view'), re_path(r'^me/exportsettings/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), - re_path(r'^me/exportsettings/user/(?P\d+)/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), + re_path(r'^me/exportsettings/user/(?P\d+)/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), re_path(r'^team/(?P\d+)/$',views.team_view,name='team_view'), re_path(r'^team/(?P\d+)/memberstats/$',views.team_members_stats_view,name='team_members_stats_view'), re_path(r'^team/(?P\d+)/edit/$',views.team_edit_view,name='team_edit_view'), @@ -500,10 +500,10 @@ urlpatterns = [ re_path(r'^me/request/(\w+.*)/$',views.manager_requests_view,name='manager_requests_view'), re_path(r'^me/request/$',views.manager_requests_view,name='manager_requests_view'), re_path(r'^me/edit/$',views.rower_edit_view,name='rower_edit_view'), - re_path(r'^me/edit/user/(?P\d+)/$',views.rower_edit_view,name='rower_edit_view'), + re_path(r'^me/edit/user/(?P\d+)/$',views.rower_edit_view,name='rower_edit_view'), re_path(r'^me/preferences/$',views.rower_prefs_view,name='rower_prefs_view'), re_path(r'^me/transactions/$',views.transactions_view,name='transactions_view'), - re_path(r'^me/preferences/user/(?P\d+)/$',views.rower_prefs_view,name='rower_prefs_view'), + re_path(r'^me/preferences/user/(?P\d+)/$',views.rower_prefs_view,name='rower_prefs_view'), re_path(r'^me/edit/(.+.*)/$',views.rower_edit_view,name='rower_edit_view'), re_path(r'^me/c2authorize/$',views.rower_c2_authorize,name='rower_c2_authorize'), re_path(r'^me/polarauthorize/$',views.rower_polar_authorize,name='rower_polar_authorize'), 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), From 489271317c7061979c68b1ae95b20ce0db786b19 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 13 Jan 2020 19:40:22 +0100 Subject: [PATCH 17/80] passing tests --- rowers/rower_rules.py | 15 ++++++--------- rowers/tests/test_permissions.py | 1 + rowers/urls.py | 2 +- rowers/views/planviews.py | 24 ++++++++++++------------ rowers/views/statements.py | 12 +++++++++--- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index bac7b6ed..d60b3682 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -103,15 +103,12 @@ def can_plan(user): # checks if rower is coach of user @rules.predicate -def is_coach_user(user,rower): - try: - r = user.rower - except AttributeError: - return False - - if rower == r: +def is_coach_user(usercoach,userrower): + if usercoach == userrower: return True + r = userrower.rower + coaches = [] for group in r.coachinggroups.all(): @@ -120,7 +117,7 @@ def is_coach_user(user,rower): coaches.append(coach) for coach in coaches: - if rower == coach: + if usercoach.rower == coach: return True return False @@ -199,7 +196,7 @@ def is_workout_user(user,workout): if workout.user == r: return True - return is_coach_user(workout.user.user,user.rower) + return is_coach_user(user,workout.user.user) # check if user can see workout @rules.predicate diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index ad2d745e..1671d82d 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -13,6 +13,7 @@ from django.db import transaction import rowers.teams as teams import rowers.plannedsessions as plannedsessions +from rowers.rower_rules import is_coach_user @override_settings(TESTING=True) class PermissionsFreeCoach(TestCase): diff --git a/rowers/urls.py b/rowers/urls.py index de413d87..b8924a1a 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -518,7 +518,7 @@ urlpatterns = [ re_path(r'^me/tprefresh/$',views.rower_tp_token_refresh,name='rower_tp_token_refresh'), re_path(r'^me/c2refresh/$',views.rower_c2_token_refresh,name='rower_c2_token_refresh'), re_path(r'^me/favoritecharts/$',views.rower_favoritecharts_view,name='rower_favoritecharts_view'), - re_path(r'^me/favoritecharts/user/(?P\d+)/$',views.rower_favoritecharts_view,name='rower_favoritecharts_view'), + re_path(r'^me/favoritecharts/user/(?P\d+)/$',views.rower_favoritecharts_view,name='rower_favoritecharts_view'), # re_path(r'^me/workflowconfig/$',views.workout_workflow_config_view), re_path(r'^me/workflowconfig2/$',views.workout_workflow_config2_view,name='workout_workflow_config2_view'), re_path(r'^me/workflowconfig2/user/(?P\d+)/$',views.workout_workflow_config2_view,name='workout_workflow_config2_view'), diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index ae16fb94..5b7b76d1 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1959,7 +1959,7 @@ class PlannedSessionDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(PlannedSessionDelete, self).get_object(*args, **kwargs) m = Rower.objects.get(user=obj.manager) - if not is_coach_user(m.user,self.request.user.rower): + if not is_coach_user(self.request.user,m.user): raise PermissionDenied('You are not allowed to delete this planned session') return obj @@ -2103,7 +2103,7 @@ def rower_delete_trainingtarget(request,id=0): except TrainingPlan.DoesNotExist: raise Http404("Training Plan Does Not Exist") - if is_coach_user(target.manager,request.user.rower): + if is_coach_user(request.user,target.manager): target.delete() messages.info(request,"We have deleted the training target") else: @@ -2123,7 +2123,7 @@ def rower_delete_trainingplan(request,id=0): except TrainingPlan.DoesNotExist: raise Http404("Training Plan Does Not Exist") - if is_coach_user(plan.manager.user,request.user.rower): + if is_coach_user(request.user,plan.manager.user): plan.delete() messages.info(request,"We have deleted the training plan") else: @@ -2140,7 +2140,7 @@ class TrainingPlanDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(TrainingPlanDelete, self).get_object(*args, **kwargs) - if not is_coach_user(obj.manager.user,self.request.user.rower): + if not is_coach_user(self.request.user,obj.manager.user): raise PermissionDenied('You are not allowed to delete this training plan') return obj @@ -2206,7 +2206,7 @@ class MicroCycleDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(MicroCycleDelete, self).get_object(*args, **kwargs) - if not is_coach_user(obj.plan.plan.plan.manager.user,self.request.user.rower): + if not is_coach_user(self.request.user,obj.plan.plan.plan.manager.user): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2268,7 +2268,7 @@ class MesoCycleDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(MesoCycleDelete, self).get_object(*args, **kwargs) - if not is_coach_user(obj.plan.plan.manager.user,self.request.user.rower): + if not is_coach_user(self.request.user,obj.plan.plan.manager.user): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2322,7 +2322,7 @@ class MacroCycleDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(MacroCycleDelete, self).get_object(*args, **kwargs) - if not is_coach_user(obj.plan.manager.user,self.request.user.rower): + if not is_coach_user(self.request.user,obj.plan.manager.user): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2345,7 +2345,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 is_coach_user(plan.manager.user,request.user.rower): + if not is_coach_user(request.user,plan.manager.user): if request.user.rower not in plan.rowers.all(): raise PermissionDenied("Access denied") @@ -2439,7 +2439,7 @@ def rower_trainingplan_view(request, r = getrequestrower(request,userid=userid) - if not is_coach_user(plan.manager.user,request.user.rower): + if not is_coach_user(request.user,plan.manager.user): if request.user.rower not in plan.rowers.all(): raise PermissionDenied("Access denied") @@ -2568,7 +2568,7 @@ class TrainingMacroCycleUpdate(UpdateView): 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 is_coach_user(obj.plan.manager.user,self.request.user.rower): + if not is_coach_user(self.request.user,obj.plan.manager.user): raise PermissionDenied('You are not allowed to edit this training plan cycle') else: obj.type = 'userdefined' @@ -2850,7 +2850,7 @@ def planmesocyclebyweek(request,id=0,userid=0): except TrainingMesoCycle.DoesNotExist: raise Http404("Training Cycle does not exist") - if not is_coach_user(cycle.plan.plan.manager.user,request.user.rower): + if not is_coach_user(request.user,cycle.plan.plan.manager.user): raise PermissionDenied("You are not allowed to do this") micros = TrainingMicroCycle.objects.filter(plan=cycle) @@ -2905,7 +2905,7 @@ def planmacrocyclebymonth(request,id=0,userid=0): except TrainingMacroCycle.DoesNotExist: raise Http404("Training Cycle does not exist") - if not is_coach_user(cycle.plan.manager.user,request.user.rower): + if not is_coach_user(request.user,cycle.plan.manager.user): raise PermissionDenied("You are not allowed to do this") mesos = TrainingMesoCycle.objects.filter(plan=cycle) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index b3820bca..b61f1d20 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -297,7 +297,13 @@ def get_workout_default_page(request,id): else: return reverse('workout_workflow_view',kwargs={'id':id}) -def get_user_by_id(request,id): +def get_user_by_id(*args,**kwargs): + request = args[0] + try: + id = args[1] + except IndexError: + id = request.user.id + return get_object_or_404(User,pk=id) def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): @@ -327,7 +333,7 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): except Rower.DoesNotExist: raise Http404("Rower doesn't exist") - if userid != 0 and not is_coach_user(u,request.user.rower): + if userid != 0 and not is_coach_user(request.user,u): raise PermissionDenied("You have no access to this user") if notpermanent == False: @@ -360,7 +366,7 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False): except Rower.DoesNotExist: raise Http404("Rower doesn't exist") - if not is_coach_user(r.user,request.user.rower): + if not is_coach_user(request.user,r.user): raise PermissionDenied("You have no access to this user") if notpermanent == False: From a4363085619d8c4f76c50d86b287da3dfc635b71 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 13 Jan 2020 21:54:05 +0100 Subject: [PATCH 18/80] added first plan test --- rowers/decorators.py | 1 - rowers/models.py | 3 +++ rowers/rower_rules.py | 22 +++++++++++++++++++++- rowers/views/statements.py | 6 ++++++ rowers/views/teamviews.py | 2 ++ rowers/views/workoutviews.py | 20 +++----------------- 6 files changed, 35 insertions(+), 19 deletions(-) diff --git a/rowers/decorators.py b/rowers/decorators.py index f9d03ed7..4bd1c7a3 100644 --- a/rowers/decorators.py +++ b/rowers/decorators.py @@ -48,4 +48,3 @@ def login_required_message(function=None, message=default_message): if function: return actual_decorator(function) return actual_decorator - diff --git a/rowers/models.py b/rowers/models.py index c0c9c520..b1f9065d 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -907,6 +907,9 @@ class Rower(models.Model): super(Rower, self).save(*args, **kwargs) + def get_managed_teams(self): + return Team.objects.filter(manager=self.user) + class DeactivateUserForm(forms.ModelForm): class Meta: model = User diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index d60b3682..40c63499 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -159,7 +159,7 @@ def can_plan_user(user,rower): rules.add_perm('rower.can_plan',can_plan_user) # replaces checkaccessplanuser rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser - +rules.add_perm('rower.is_pro',ispromember) # WORKOUT permissions @@ -240,6 +240,26 @@ rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutu """ +# check if user has view access to session +@rules.predicate +def can_view_session(user,session): + # session manager can view session + if user == session.manager: + return True + # if you're a rower in the session you can view it + if user.rower in session.rower.all(): + return True + # coach users can view sessions created by their team members + if is_coach(user): + teams = user.rower.get_managed_teams() + teamusers = [member.u for member in t.rower.all()] + if session.manager in teamusers: + return True + + return False + +rules.add_perm('plannedsession.view_session',can_view_session) + # checkaccessplanuser (models.py) # getrequestrower, getrequestplanrower diff --git a/rowers/views/statements.py b/rowers/views/statements.py index b61f1d20..5d3a969d 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -282,11 +282,17 @@ def getfavorites(r,row): return favorites,maxfav +def get_promember(request,*args,**kwargs): + return request.user + 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,id): + return get_object_or_404(PlannedSession,pk=id) + def get_workout_default_page(request,id): if request.user.is_anonymous: return reverse('workout_view',kwargs={'id':id}) diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 3e9d942e..0667b472 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -10,6 +10,8 @@ from rowers.rower_rules import * def get_team_by_pk(request,team_id): return get_object_or_404(Team,pk=team_id) + + #@login_required() @permission_required('teams.view_team',fn=get_team_by_pk,raise_exception=True) def team_view(request,team_id=0,userid=0): diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 4d70bfa3..a8628322 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -1129,9 +1129,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, @@ -1564,7 +1561,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) @@ -1573,18 +1571,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) @@ -5876,7 +5862,7 @@ class VideoDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(VideoDelete, self).get_object(*args, **kwargs) - if not is_coach_user(obj.workout.user,self.request.user): + if not is_coach_user(self.request.user,obj.workout.user): raise PermissionDenied('You are not allowed to delete this analysis') return obj From f627b5e925b7140104063ff3927784a028eab139 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 14 Jan 2020 15:59:53 +0100 Subject: [PATCH 19/80] half way through planviews, added can_change_session --- rowers/rower_rules.py | 17 ++++ rowers/templatetags/rowerfilters.py | 8 +- rowers/urls.py | 4 +- rowers/views/analysisviews.py | 2 +- rowers/views/planviews.py | 122 ++++++++-------------------- rowers/views/statements.py | 19 +---- 6 files changed, 57 insertions(+), 115 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 40c63499..74709c9d 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -243,6 +243,10 @@ rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutu # check if user has view access to session @rules.predicate def can_view_session(user,session): + if session.sessiontype in ['race','indoorrace']: + return True + if user.is_anonymous: + return False # session manager can view session if user == session.manager: return True @@ -258,7 +262,20 @@ def can_view_session(user,session): return False +@rules.predicate +def can_change_session(user,session): + if user.is_anonymous: + return False + # session part of a race should not be changed through the session interface + if session.sessiontype in ['race','indoorrace']: + return False + if user == session.manager: + return True + + return False + rules.add_perm('plannedsession.view_session',can_view_session) +rules.add_perm('plannedsession.change_session',can_change_session) # checkaccessplanuser (models.py) # getrequestrower, getrequestplanrower diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index cb0ca2d2..60b02cbd 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -21,7 +21,7 @@ from rowers.plannedsessions import ( from rowers import c2stuff, runkeeperstuff from rowers.c2stuff import c2_open from rowers.runkeeperstuff import runkeeper_open -from rowers.rower_rules import is_coach_user, is_workout_user +from rowers.rower_rules import is_coach_user, is_workout_user, isplanmember,ispromember from rowers.mytypes import otwtypes from rowers.utils import NoTokenError @@ -430,7 +430,7 @@ def get_field_id(id,s,form): from rowers.models import Rower,Team,TrainingPlan,TrainingTarget -from rowers.views import ispromember + @register.filter def is_promember(user): return ispromember(user) @@ -440,10 +440,10 @@ def is_manager(user): r = Rower.objects.get(user=user) return 'coach' in r.rowerplan -from rowers.views import hasplannedsessions + @register.filter def is_planmember(user): - return hasplannedsessions(user) + return isplanmember(user) @register.filter def get_age(r): diff --git a/rowers/urls.py b/rowers/urls.py index b8924a1a..b8c65fb6 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -635,9 +635,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, diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 4cf62a9e..d7792db5 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -1329,7 +1329,7 @@ def planrequired_view(request): return HttpResponseRedirect(reverse('paidplans')) -@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', diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 5b7b76d1..c5c6d9ac 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(isplanmember,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(isplanmember,login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) def plannedsession_create_view(request, @@ -477,7 +461,7 @@ def plannedsession_create_view(request, 'timeperiod':timeperiod, }) -@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 plannedsession_multicreate_view(request, @@ -613,7 +597,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(isplanmember,login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) def plannedsession_teamcreate_view(request, @@ -786,21 +770,16 @@ def plannedsession_teamcreate_view(request, }) # Manager edits sessions for entire team -@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) +@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] @@ -1402,7 +1381,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(isplanmember,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 +1400,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() @@ -1470,7 +1444,8 @@ def plannedsession_clone_view(request,id=0,userid=0): # Clone an existing planned session # need clarity on cloning behavior time shift -@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/", +@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) +@user_passes_test(isplanmember,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 +1472,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 @@ -1534,7 +1503,8 @@ def plannedsession_teamclone_view(request,id=0): return HttpResponseRedirect(url) -@user_passes_test(hasplannedsessions, login_url="/rowers/paidplans/", +@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) +@user_passes_test(isplanmember, 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): @@ -1543,13 +1513,7 @@ def plannedsession_totemplate_view(request,id=0): startdate, enddate = get_dates_timeperiod(request) - try: - ps = PlannedSession.objects.get(id=id) - except PlannedSession.DoesNotExist: - raise Http404("Planned Session does not exist") - - if ps.manager != request.user: - raise PermissionDenied("You can only store your own sessions as a template") + ps = get_object_or_404(PlannedSession,pk=id) ps.pk = None ps.id = None @@ -1567,7 +1531,8 @@ def plannedsession_totemplate_view(request,id=0): return HttpResponseRedirect(url) # Edit an existing planned session -@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/", +@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) +@user_passes_test(isplanmember,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): @@ -1587,16 +1552,7 @@ def plannedsession_edit_view(request,id=0,userid=0): except IndexError: trainingplan = None - try: - ps = PlannedSession.objects.get(id=id) - except PlannedSession.DoesNotExist: - raise Http404("Planned Session does not exist") - - if ps.manager != request.user: - raise PermissionDenied("You are not allowed to edit this planned session") - - if ps.sessiontype in ['race','indoorrace']: - raise PermissionDenied("You are not allowed to edit this planned session because it is a race") + ps = get_object_or_404(PlannedSession,pk=id) if ps.team.all() or ps.rower.all().count()>1: url = reverse(plannedsession_teamedit_view, @@ -1714,16 +1670,12 @@ def plannedsession_detach_view(request,id=0,psid=0): return HttpResponseRedirect(url) @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', @@ -1740,16 +1692,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() @@ -1965,7 +1907,7 @@ class PlannedSessionDelete(DeleteView): return obj -@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 rower_create_trainingplan(request,userid=0): @@ -2094,7 +2036,7 @@ def rower_create_trainingplan(request,userid=0): 'old_targets':old_targets, }) -@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 rower_delete_trainingtarget(request,id=0): @@ -2114,7 +2056,7 @@ def rower_delete_trainingtarget(request,id=0): return HttpResponseRedirect(url) -@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 rower_delete_trainingplan(request,id=0): @@ -2328,7 +2270,7 @@ class MacroCycleDelete(DeleteView): return obj -@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 rower_trainingplan_execution_view(request, @@ -2418,7 +2360,7 @@ def rower_trainingplan_execution_view(request, ) -@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 rower_trainingplan_view(request, @@ -2841,7 +2783,7 @@ class TrainingTargetUpdate(UpdateView): from rowers.utils import allsundays -@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 planmesocyclebyweek(request,id=0,userid=0): @@ -2896,7 +2838,7 @@ def planmesocyclebyweek(request,id=0,userid=0): from rowers.utils import allmonths -@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 planmacrocyclebymonth(request,id=0,userid=0): diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 5d3a969d..315f4df4 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -40,7 +40,7 @@ from rowers.opaque import encoder from rowers.rower_rules import ( ispromember,is_coach_user,is_team_member,is_rower_team_member, - is_workout_user + is_workout_user,isplanmember, ) from django.shortcuts import render @@ -289,7 +289,6 @@ 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,id): return get_object_or_404(PlannedSession,pk=id) @@ -1014,22 +1013,6 @@ def cancreateteam(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 abb7b85f748a6072cb51e2fb7082d4aec5e8a2e3 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 14 Jan 2020 19:22:03 +0100 Subject: [PATCH 20/80] implemented changes in planviews - not tested --- rowers/rower_rules.py | 114 ++++++++++++++++++++++++++++++++++++- rowers/views/planviews.py | 82 +++++++------------------- rowers/views/statements.py | 27 ++++++++- 3 files changed, 160 insertions(+), 63 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 74709c9d..2697ac2c 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -240,11 +240,107 @@ rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutu """ +# Training Target rules +@rules.predicate +def can_view_target(user,target): + if user.is_anonymous: + return False + if user == target.manager.user: + return True + + # a target's coach can view as well + if is_coach_user(user,target.manager.user): + return True + + # the object can view as well + if user.rower in target.rowers.all(): + return True + +@rules.predicate +def can_change_target(user,target): + if user.is_anonymous: + return False + return user == target.manager.user + +@rules.predicate +def can_delete_target(user,target): + if user.is_anonymous: + return False + return user == target.manager.user + +rules.add_perm('target.view_target',can_view_target) +rules.add_perm('target.change_target',can_change_target) +rules.add_perm('target.delete_target',can_delete_target) + +@rules.predicate +def can_view_plan(user,plan): + if user.is_anonymous: + return False + if user == plan.manager.user: + return True + + # a plan's coach can view as well + if is_coach_user(user,plan.manager.user): + return True + + # the object can view as well + if user.rower in plan.rowers.all(): + return True + +@rules.predicate +def can_change_plan(user,plan): + if user.is_anonymous: + return False + return user == plan.manager.user + +@rules.predicate +def can_delete_plan(user,plan): + if user.is_anonymous: + return False + return user == plan.manager.user + +rules.add_perm('plan.view_plan',can_view_plan) +rules.add_perm('plan.change_plan',can_change_plan) +rules.add_perm('plan.delete_plan',can_delete_plan) + +@rules.predicate +def can_view_cycle(user,cycle): + try: + return can_view_cycle(user,cycle.plan) + except AttributeError: + return can_view_plan(user,cycle.plan) + + return False + +@rules.predicate +def can_change_cycle(user,cycle): + try: + return can_change_cycle(user,cycle.plan) + except AttributeError: + return can_change_plan(user,cycle.plan) + + return False + +@rules.predicate +def can_delete_cycle(user,cycle): + try: + return can_delete_cycle(user,cycle.plan) + except AttributeError: + return can_delete_plan(user,cycle.plan) + + return False + + +rules.add_perm('cycle.view_cycle',can_view_cycle) +rules.add_perm('cycle.change_cycle',can_change_cycle) +rules.add_perm('cycle.delete_cycle',can_delete_cycle) + + # check if user has view access to session @rules.predicate def can_view_session(user,session): if session.sessiontype in ['race','indoorrace']: - return True + return True if user.is_anonymous: return False # session manager can view session @@ -274,8 +370,24 @@ def can_change_session(user,session): return False +@rules.predicate +def can_delete_session(user,session): + if user.is_anonymous: + return False + + if session.sessiontype in ['race','indoorrace']: + return False + + if user == session.manager: + return True + + return False + rules.add_perm('plannedsession.view_session',can_view_session) rules.add_perm('plannedsession.change_session',can_change_session) +rules.add_perm('plannedsession.delete_session',can_delete_session) + + # checkaccessplanuser (models.py) # getrequestrower, getrequestplanrower diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index c5c6d9ac..26f6aa22 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1900,8 +1900,7 @@ class PlannedSessionDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(PlannedSessionDelete, self).get_object(*args, **kwargs) - m = Rower.objects.get(user=obj.manager) - if not is_coach_user(self.request.user,m.user): + if not can_delete_session(self.request.user,obj): raise PermissionDenied('You are not allowed to delete this planned session') return obj @@ -2039,17 +2038,11 @@ def rower_create_trainingplan(request,userid=0): @user_passes_test(isplanmember,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 is_coach_user(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) @@ -2059,17 +2052,10 @@ def rower_delete_trainingtarget(request,id=0): @user_passes_test(isplanmember,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 is_coach_user(request.user,plan.manager.user): - 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) @@ -2082,7 +2068,7 @@ class TrainingPlanDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(TrainingPlanDelete, self).get_object(*args, **kwargs) - if not is_coach_user(self.request.user,obj.manager.user): + if not can_delete_plan(self.request.user,obj): raise PermissionDenied('You are not allowed to delete this training plan') return obj @@ -2148,7 +2134,7 @@ class MicroCycleDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(MicroCycleDelete, self).get_object(*args, **kwargs) - if not is_coach_user(self.request.user,obj.plan.plan.plan.manager.user): + if not can_delete_cycle(self.request.user,obj): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2210,7 +2196,7 @@ class MesoCycleDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(MesoCycleDelete, self).get_object(*args, **kwargs) - if not is_coach_user(self.request.user,obj.plan.plan.manager.user): + if not can_delete_cycle(self.request.user,obj): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2264,7 +2250,7 @@ class MacroCycleDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(MacroCycleDelete, self).get_object(*args, **kwargs) - if not is_coach_user(self.request.user,obj.plan.manager.user): + if not can_delete_cycle(self.request.user,obj): raise PermissionDenied('You are not allowed to delete this training plan cycle') return obj @@ -2363,6 +2349,7 @@ def rower_trainingplan_execution_view(request, @user_passes_test(isplanmember,login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) +@permission_required('plan.can_view',fn=get_plan_by_pk,raise_exception=True) def rower_trainingplan_view(request, id=0, userid=0, @@ -2373,17 +2360,7 @@ 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") - - r = getrequestrower(request,userid=userid) - - if not is_coach_user(request.user,plan.manager.user): - if request.user.rower not in plan.rowers.all(): - raise PermissionDenied("Access denied") + plan = get_object_or_404(TrainingPlan,pk=id) createmacrofillers(plan) macrocycles = TrainingMacroCycle.objects.filter( @@ -2507,10 +2484,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 is_coach_user(self.request.user,obj.plan.manager.user): + 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' @@ -2581,7 +2555,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: @@ -2659,7 +2633,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') @@ -2724,10 +2698,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 @@ -2776,7 +2748,7 @@ 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 @@ -2786,14 +2758,9 @@ from rowers.utils import allsundays @user_passes_test(isplanmember,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 is_coach_user(request.user,cycle.plan.plan.manager.user): - 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: @@ -2841,14 +2808,9 @@ from rowers.utils import allmonths @user_passes_test(isplanmember,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 is_coach_user(request.user,cycle.plan.manager.user): - 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/statements.py b/rowers/views/statements.py index 315f4df4..3048a288 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -40,7 +40,9 @@ 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, + 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, ) from django.shortcuts import render @@ -289,9 +291,30 @@ 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,id): +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}) From 5482e5223881414529a04161a42431e935cca747 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 14 Jan 2020 21:25:19 +0100 Subject: [PATCH 21/80] make it pass tests (planviews) --- rowers/rower_rules.py | 2 +- rowers/tests/test_rowerplans.py | 14 ++++++++------ rowers/views/planviews.py | 10 +++++----- rowers/views/statements.py | 1 + 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 2697ac2c..34f6f25e 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -85,7 +85,7 @@ def is_plantrial(user): except AttributeError: return False - if r.rowerplan == 'basic': + if r.rowerplan in ['basic','pro']: return r.plantrialexpires >= datetime.date.today() if r.rowerplan == 'freecoach': if r.mycoachgroup is not None: diff --git a/rowers/tests/test_rowerplans.py b/rowers/tests/test_rowerplans.py index d87f1a56..557d148b 100644 --- a/rowers/tests/test_rowerplans.py +++ b/rowers/tests/test_rowerplans.py @@ -6,7 +6,8 @@ from __future__ import unicode_literals from .statements import * nu = datetime.datetime.now() -from rowers.views import hasplannedsessions,iscoachmember,ispromember +from rowers.views import isplanmember,iscoachmember,ispromember +from rowers.rower_rules import * class TrialsTest(TestCase): def setUp(self): @@ -33,34 +34,35 @@ class TrialsTest(TestCase): pass def test_basictrial(self): - self.assertEqual(hasplannedsessions(self.u),False) + self.assertEqual(isplanmember(self.u),False) self.assertEqual(iscoachmember(self.u),False) self.assertEqual(ispromember(self.u),False) self.r.protrialexpires = (nu+datetime.timedelta(days=10)).date() self.r.save() - self.assertEqual(hasplannedsessions(self.u),False) + self.assertEqual(isplanmember(self.u),False) self.assertEqual(iscoachmember(self.u),False) self.assertEqual(ispromember(self.u),True) self.r.plantrialexpires = (nu+datetime.timedelta(days=10)).date() self.r.save() - self.assertEqual(hasplannedsessions(self.u),True) + self.assertEqual(isplanmember(self.u),True) self.assertEqual(iscoachmember(self.u),False) self.assertEqual(ispromember(self.u),True) def test_protrial(self): self.r.rowerplan = 'pro' self.r.save() - self.assertEqual(hasplannedsessions(self.u),False) + self.assertEqual(isplanmember(self.u),False) self.assertEqual(iscoachmember(self.u),False) self.assertEqual(ispromember(self.u),True) self.r.plantrialexpires = (nu+datetime.timedelta(days=10)).date() self.r.save() - self.assertEqual(hasplannedsessions(self.u),True) + self.assertEqual(is_plantrial(self.u),True) + self.assertEqual(isplanmember(self.u),True) self.assertEqual(iscoachmember(self.u),False) self.assertEqual(ispromember(self.u),True) diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 26f6aa22..097af6f2 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -869,7 +869,7 @@ def plannedsession_teamedit_view(request, url = reverse(plannedsession_teamedit_view, kwargs = { - 'sessionid':sessionid, + 'id':id, }) startdatestring = startdate.strftime('%Y-%m-%d') @@ -1491,7 +1491,7 @@ def plannedsession_teamclone_view(request,id=0): url = reverse(plannedsession_teamedit_view, kwargs = { - 'sessionid':ps.id, + 'id':ps.id, 'userid':r.user.id, } ) @@ -1557,7 +1557,7 @@ def plannedsession_edit_view(request,id=0,userid=0): if ps.team.all() or ps.rower.all().count()>1: url = reverse(plannedsession_teamedit_view, kwargs={ - 'sessionid':id, + 'id':id, }) return HttpResponseRedirect(url) @@ -1910,7 +1910,6 @@ class PlannedSessionDelete(DeleteView): 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) theuser = therower.user themanager = getrower(request.user) @@ -2349,7 +2348,7 @@ def rower_trainingplan_execution_view(request, @user_passes_test(isplanmember,login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -@permission_required('plan.can_view',fn=get_plan_by_pk,raise_exception=True) +@permission_required('plan.view_plan',fn=get_plan_by_pk,raise_exception=True) def rower_trainingplan_view(request, id=0, userid=0, @@ -2361,6 +2360,7 @@ def rower_trainingplan_view(request, startdate,enddate = get_dates_timeperiod(request) plan = get_object_or_404(TrainingPlan,pk=id) + r = getrequestrower(request,userid=userid) createmacrofillers(plan) macrocycles = TrainingMacroCycle.objects.filter( diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 3048a288..2955d3c7 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -43,6 +43,7 @@ from rowers.rower_rules import ( 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 ) from django.shortcuts import render From 0cff00eb18cfa087a62ebc789a92648462710b8c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 14 Jan 2020 21:29:50 +0100 Subject: [PATCH 22/80] changing to convention --- rowers/rower_rules.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 34f6f25e..97f9ed0d 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -434,12 +434,19 @@ def can_view_team(user,team): # team is private return is_team_member(user,team) | is_team_manager(user,team) +@rules.predicate +def can_change_team(user,team): + return is_team_manager(user,team) + +@rules.predicate +def can_delete_team(user,team): + return is_team_manager(user,team) # For Team functionality rules.add_perm('teams.view_team',can_view_team) rules.add_perm('teams.add_team',user_is_not_basic) -rules.add_perm('teams.change_team',is_team_manager) -rules.add_perm('teams.delete_team',is_team_manager) +rules.add_perm('teams.change_team',can_change_team) +rules.add_perm('teams.delete_team',can_delete_team) # RACING permissions From 62fe0252184f62df92f3904ea7bdf8933668120a Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 14 Jan 2020 22:19:29 +0100 Subject: [PATCH 23/80] worked through a few more views. Need to do analysis, api, error --- rowers/rower_rules.py | 41 ++++- rowers/urls.py | 2 +- rowers/views/racesviews.py | 322 ++++++++++++++++--------------------- rowers/views/statements.py | 12 ++ 4 files changed, 189 insertions(+), 188 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 97f9ed0d..7ebfc732 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -387,11 +387,6 @@ rules.add_perm('plannedsession.view_session',can_view_session) rules.add_perm('plannedsession.change_session',can_change_session) rules.add_perm('plannedsession.delete_session',can_delete_session) - - -# checkaccessplanuser (models.py) -# getrequestrower, getrequestplanrower - # TEAM (group) permissions """ @@ -455,8 +450,44 @@ rules.add_perm('teams.delete_team',can_delete_team) - rules to add, view, delete, change - GeoCourse - rules to add, view, delete, change + - RaceLogo """ +@rules.predicate +def can_change_course(user,course): + if user.is_anonymous: + return False + + return course.manager == user.rower + +@rules.predicate +def can_delete_course(user,course): + if user.is_anonymous: + return False + + return course.manager == user.rower + +@rules.predicate +def can_delete_logo(user,logo): + if user.is_anonymous: + return False + + return logo.user == user + +@rules.predicate +def can_change_race(user,race): + if user.is_anonymous: + return False + + return race.manager == user + +rules.add_perm('course.change_course',can_change_course) +rules.add_perm('course.delete_course',can_delete_course) + +rules.add_perm('racelogo.delete_logo',can_delete_logo) + +rules.add_perm('virtualevent.change_race',can_change_race) + # ANALYSIS permissions diff --git a/rowers/urls.py b/rowers/urls.py index b8c65fb6..4a890095 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -191,7 +191,7 @@ urlpatterns = [ views.virtualevent_submit_result_view,name='virtualevent_submit_result_view'), re_path(r'^virtualevent/(?P\d+)/submit/(?P\b[0-9A-Fa-f]+\b)/$', views.virtualevent_submit_result_view,name='virtualevent_submit_result_view'), - re_path(r'^virtualevent/(?P\d+)/disqualify/(?P\d+)/', + re_path(r'^virtualevent/(?P\d+)/disqualify/(?P\d+)/', views.virtualevent_disqualify_view,name='virtualevent_disqualify_view'), re_path(r'^list-workouts/$',views.workouts_view,name='workouts_view'), re_path(r'^list-courses/$',views.courses_view,name='courses_view'), diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py index 1cc53312..358afddd 100644 --- a/rowers/views/racesviews.py +++ b/rowers/views/racesviews.py @@ -45,7 +45,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 +64,10 @@ def course_map_view(request,id=0): 'name': 'Map' } ] - + r = getrower(request.user) - - + + return render(request, 'coursemap.html', { @@ -76,21 +76,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 +136,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 +153,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 +172,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 +198,7 @@ def course_edit_view(request,id=0): 'name': 'Edit' } ] - + return render(request, 'course_edit_view.html', { 'course':course, @@ -235,9 +220,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 +251,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 +265,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 +292,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 +319,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 +370,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 +396,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 +406,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 +414,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 +463,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 +495,7 @@ def virtualevents_view(request): country__in=countries ) - + races = (races1 | races2).order_by("startdate","start_time") @@ -550,7 +521,7 @@ def virtualevents_view(request): country__in=countries ).order_by("startdate","start_time") else: - + form = VirtualRaceSelectForm() if is_ajax: @@ -558,7 +529,7 @@ def virtualevents_view(request): { 'races':races, 'rower':r, }) - + breadcrumbs = [ { 'url':reverse('virtualevents_view'), @@ -576,18 +547,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 +569,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 +585,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 +671,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 +689,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 +710,8 @@ def virtualevent_view(request,id=0): resultobj = IndoorVirtualRaceResult records = resultobj.objects.filter(race=race) - - + + buttons = [] # to-do - add DNS @@ -755,7 +721,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 +756,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 +800,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 +826,9 @@ def virtualevent_view(request,id=0): form = RaceResultFilterForm(records=records) else: form = None - - + + breadcrumbs = [ { 'url':reverse('virtualevents_view'), @@ -875,7 +841,7 @@ def virtualevent_view(request,id=0): 'name': race.name } ] - + racelogos = race.logos.all() if racelogos: @@ -885,7 +851,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 +872,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 +893,8 @@ def virtualevent_ranking_view(request,id=0): resultobj = IndoorVirtualRaceResult records = resultobj.objects.filter(race=race) - - + + buttons = [] # to-do - add DNS @@ -938,7 +904,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 +939,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 +983,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 +1009,9 @@ def virtualevent_ranking_view(request,id=0): form = RaceResultFilterForm(records=records) else: form = None - - + + breadcrumbs = [ { 'url':reverse('virtualevents_view'), @@ -1058,7 +1024,7 @@ def virtualevent_ranking_view(request,id=0): 'name': race.name } ] - + racelogos = race.logos.all() if racelogos: @@ -1132,7 +1098,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 +1117,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 +1133,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 +1165,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 +1181,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 +1209,8 @@ def virtualevent_addboat_view(request,id=0): 'name': 'Add Discipline' } ] - - + + buttons = [] if not request.user.is_anonymous: @@ -1311,7 +1277,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 +1304,7 @@ def virtualevent_register_view(request,id=0): ) record.save() - + add_rower_race(r,race) otherrecords = IndoorVirtualRaceResult.objects.filter( @@ -1357,8 +1323,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 +1336,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 +1385,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 +1406,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 +1420,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 +1428,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 +1469,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 +1495,7 @@ def indoorvirtualevent_register_view(request,id=0): ) record.save() - + add_rower_race(r,race) otherrecords = IndoorVirtualRaceResult.objects.filter( @@ -1548,8 +1514,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 +1527,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 +1590,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 +1616,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 +1647,7 @@ def indoorvirtualevent_create_view(request): sessionmode = 'time' else: sessionmode = 'distance' - + vs = VirtualRace( name=name, startdate=startdate, @@ -1719,7 +1685,7 @@ def indoorvirtualevent_create_view(request): ), dotweet = dotweet ) - + sa.save() url = reverse('virtualevents_view') @@ -1728,7 +1694,7 @@ def indoorvirtualevent_create_view(request): racecreateform = IndoorVirtualRaceForm(timezone=r.defaulttimezone) - + breadcrumbs = [ { 'url':reverse('virtualevents_view'), @@ -1753,7 +1719,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 +1745,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 +1771,8 @@ def virtualevent_create_view(request): registration_closure = enddatetime else: registration_closure = evaluation_closure - - + + vs = VirtualRace( name=name, startdate=startdate, @@ -1842,7 +1808,7 @@ def virtualevent_create_view(request): ), dotweet = dotweet ) - + sa.save() url = reverse('virtualevents_view') return HttpResponseRedirect(url) @@ -1850,7 +1816,7 @@ def virtualevent_create_view(request): racecreateform = VirtualRaceForm() - + breadcrumbs = [ { 'url':reverse('virtualevents_view'), @@ -1872,15 +1838,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 +1863,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 +1875,7 @@ def virtualevent_edit_view(request,id=0): kwargs = { 'id':race.id }) - + return HttpResponseRedirect(url) else: @@ -1937,7 +1900,7 @@ def virtualevent_edit_view(request,id=0): 'name': 'Edit' } ] - + buttons = [] if not request.user.is_anonymous: @@ -1970,15 +1933,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 +1956,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 +1968,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 +2015,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 +2042,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 +2054,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 +2062,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 +2079,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 +2092,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 +2109,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 +2141,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 +2175,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 +2231,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 2955d3c7..43fa3315 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -285,9 +285,21 @@ 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) From 7aa03b71ed18c7a3780c7a58959ea4517cad8fb2 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 14 Jan 2020 22:21:12 +0100 Subject: [PATCH 24/80] some annotations --- rowers/rower_rules.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 7ebfc732..ca889645 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -481,12 +481,15 @@ def can_change_race(user,race): return race.manager == user +# everybody can view or add a course rules.add_perm('course.change_course',can_change_course) rules.add_perm('course.delete_course',can_delete_course) rules.add_perm('racelogo.delete_logo',can_delete_logo) +# everybody can view a race rules.add_perm('virtualevent.change_race',can_change_race) +# can races be deleted? # ANALYSIS permissions From a6746030e9cc381fb95a05a8ec4b3a72441069dc Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 15 Jan 2020 10:17:46 +0100 Subject: [PATCH 25/80] added inventory from test_permissions --- rowers/models.py | 71 +++++++++-------- rowers/rower_rules.py | 131 +++++++++++++++++++++++++++++-- rowers/teams.py | 10 +-- rowers/tests/test_permissions.py | 8 +- rowers/views/planviews.py | 2 +- rowers/views/statements.py | 3 +- rowers/views/workoutviews.py | 2 +- 7 files changed, 170 insertions(+), 57 deletions(-) diff --git a/rowers/models.py b/rowers/models.py index b1f9065d..e2083164 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -41,7 +41,7 @@ import datetime #from rules.contrib.models import RulesModel - +from rowers.rower_rules import * from rowers.rows import validate_file_extension from collections import OrderedDict @@ -330,12 +330,6 @@ class C2WorldClassAgePerformance(models.Model): return thestring -def is_not_basic(user): - if user.rower.rowerplan == 'basic': - raise ValidationError( - "Basic user cannot be team manager" - ) - @python_2_unicode_compatible class Team(models.Model): @@ -351,7 +345,7 @@ class Team(models.Model): name = models.CharField(max_length=150,unique=True,verbose_name='Team Name') notes = models.CharField(blank=True,max_length=200,verbose_name='Team Purpose') - manager = models.ForeignKey(User, null=True,on_delete=models.CASCADE) # validators=[is_not_basic]) + manager = models.ForeignKey(User, null=True,on_delete=models.CASCADE) private = models.CharField(max_length=30,choices=choices,default='open', verbose_name='Team Type') @@ -364,18 +358,15 @@ class Team(models.Model): def save(self, *args, **kwargs): manager = self.manager - if manager.rower.rowerplan == 'basic': - if manager.rower.protrialexpires < datetime.date.today() and manager.rower.plantrialexpires < datetime.date.today(): + if not can_have_teams(manager): raise ValidationError( "Basic user cannot be team manager" ) - if manager.rower.rowerplan in ['plan','pro']: - otherteams = Team.objects.filter(manager=manager) - if otherteams.count() >= 1: - raise ValidationError( - "Pro and Self-Coach users cannot have more than one team" - ) + if not can_add_team(manager): + raise ValidationError( + "Pro and Self-Coach users cannot have more than one team" + ) super(Team, self).save(*args,**kwargs) @@ -910,6 +901,17 @@ class Rower(models.Model): def get_managed_teams(self): return Team.objects.filter(manager=self.user) + def get_coaches(self): + coaches = [] + for group in self.coachinggroups.all(): + try: + coach = Rower.objects.get(mycoachgroup=group) + coaches.append(coach) + except Rower.DoesNotExist: + pass + + return coaches + class DeactivateUserForm(forms.ModelForm): class Meta: model = User @@ -943,14 +945,14 @@ def check_teams_on_change(sender, **kwargs): instance = kwargs.pop('instance', None) action = kwargs.pop('action', None) pk_set = kwargs.pop('pk_set',None) - if action == 'pre_add' and instance.rowerplan=='basic': - if instance.protrialexpires < datetime.date.today() and instance.plantrialexpires < datetime.date.today(): - for id in pk_set: - team = Team.objects.get(id=id) - if team.manager.rower.rowerplan not in ['coach']: - raise ValidationError( - "You cannot join a team led by a Pro, Free Coach Plan or Self-Coach user" - ) + if action == 'pre_add': + #if instance.protrialexpires < datetime.date.today() and instance.plantrialexpires < datetime.date.today(): + for id in pk_set: + team = Team.objects.get(id=id) + if not can_join_team(instance,team): + raise ValidationError( + "You cannot join a team led by a Pro, Free Coach Plan or Self-Coach user" + ) m2m_changed.connect(check_teams_on_change, sender=Rower.team.through) @@ -1349,12 +1351,10 @@ class TrainingPlan(models.Model): def save(self, *args, **kwargs): manager = self.manager - - if manager.rowerplan in ['basic','pro']: - if manager.plantrialexpires < timezone.now().date(): - raise ValidationError( - "Basic user cannot have a training plan" - ) + if not can_add_plan(manager): + raise ValidationError( + "Basic user cannot have a training plan" + ) if self.enddate < self.startdate: startdate = self.startdate @@ -2084,11 +2084,10 @@ class PlannedSession(models.Model): manager = self.manager if self.sessiontype not in ['race','indoorrace']: - if manager.rower.rowerplan in ['basic','pro']: - if manager.rower.plantrialexpires < timezone.now().date(): - raise ValidationError( - "You must be a Self-Coach user or higher to create a planned session" - ) + if not can_add_session(self.manager): + raise ValidationError( + "You must be a Self-Coach user or higher to create a planned session" + ) # sort units @@ -2666,7 +2665,7 @@ class Workout(models.Model): def save(self, *args, **kwargs): user = self.user - if self.user.rowerplan == 'freecoach': + if not can_add_workout(self.user): raise forms.ValidationError("Free Coach User cannot have any workouts") super(Workout, self).save(*args, **kwargs) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index ca889645..c4e58792 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -22,10 +22,53 @@ USER permissions - rower.team - These Rules apply to Rowers + - Coach can have any number of groups + - test_permissions.PermissionFreeCoach.test_coach_groupmanager + - test_permissions.PermissionsBasicsTests.test_coach_groupmanager + - test_permissions.PermissionsViewTests.test_coach_groups_create + - Pro and Plan users can have one group and not more + - test_permissions.PermissionsFreeCoach.test_pro_groupmanager + - test_permissions.PermissionsViewTests.test_pro_groups_create - Free Coach user cannot have workouts + - test_permissions.PermissionFreeCoach.test_add_workout_freecoach + - Free coach can create more than one group + - test_permissions.PermissionsFreeCoach.test_plan_groupmanager + - Free Coach & Plan (Self Coach) can create planned sessions and team planned sessions + - test_permissions.PermissionsFreeCoach.test_plan_create_session + - test_permissions.PermissionsFreeCoach.test_coach_create_session + - Pro cannot create planned sessions or team planned sessions + - test_permissions.PermissionsViewTests.test_pro_create_session + - Basic cannot join groups led by Free Coach + - test_permissions.PermissionFreeCoach.test_add_basic_pro_or_plan - Coach can edit athlete settings (if in coachinggroup) + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_prefs + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_prefs_not + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_settings + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_settings_not + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_account + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_account_not + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_exportsettings + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_exportsettings_not + - Coach can upload a workout on behalf of athlete + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload_not + - Pro and Plan cannot upload a workout on behalf of athlete + - test_permissions.PermissionsViewTests.test_plan_edit_athlete_upload - Coach can run analytics for athlete + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_analysis + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_analysis_not + - test_permissions.PermissionsViewTests.test_pro_edit_athlete_analysis - Pro and Plan user cannot run analysis for members of their groups + - test_permissions.PermissionsViewTests.test_plan_edit_athlete_analysis + - Pro and Plan user cannot edit athlete sessings + - test_permissions.PermissionsViewTests.test_plan_edit_athlete_settings + - test_permissions.PermissionsViewTests.test_pro_edit_athlete_settings + - Self-Coach and Pro cannot create more than one group + - test_permissions.PermissionsFreeCoach.test_plan_groupmanager + - Pro users and higher can join group led by other Pro or higher user + - test_permissions.PermissionsFreeCoach.test_add_proplan_pro_or_plan + - Pro or basic cannot create planned sessions or team planned sessions + - test_permissions.PermissionsFreeCoach.test_pro_create_plannedsession """ @@ -43,6 +86,9 @@ def user_is_not_basic(user): def is_coach(user): return user.rower.rowerplan in ['coach','freecoach'] +def is_paidcoach(user): + return user.rower.rowerplan == 'coach' + @rules.predicate def is_planmember(user): try: @@ -78,6 +124,31 @@ def is_protrial(user): ispromember = is_promember | is_protrial +can_have_teams = ispromember + +@rules.predicate +def can_add_team(user): + if is_coach(user): + return True + + if ispromember(user): + otherteams = user.rower.get_managed_teams() + if otherteams.count() == 0: + return True + + return False + +@rules.predicate +def can_add_plan(user): + return isplanmember(user) + +@rules.predicate +def can_add_workout(user): + if user.is_anonymous: + return False + + return user.rower.rowerplan != 'freecoach' + @rules.predicate def is_plantrial(user): try: @@ -93,8 +164,13 @@ def is_plantrial(user): return False + isplanmember = is_planmember | is_plantrial +@rules.predicate +def can_add_session(user): + return isplanmember(user) + # User / Coach relationships (Rower object) @rules.predicate @@ -139,6 +215,16 @@ def is_rower_team_member(user,rower): return False +@rules.predicate +def can_add_workout_member(user,rower): + if not user: + return False + if user.is_anonymous: + return False + if user == rower.user: + return True + return isplanmember(user) and user.rower in rower.get_coaches() + # check if user can plan for the rower @rules.predicate def can_plan_user(user,rower): @@ -170,10 +256,17 @@ WORKOUT permissions - These rules apply to workouts - User can add, delete and change their own workouts + - test_aworkouts - Coach can add and change workouts for their athletes, but not delete - - Pro and Plan user cannot add or change an athlete's workout - - Pro, Basic users can view team members' workout + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload_not + - Basic, Pro and Plan user can view but cannot add or change an athlete's workout + - test_permissions.PermissionsViewTests.test_coach_edit_athlete_workout + - test_permissions.PermissionsViewTests.test_plan_edit_athlete_upload + - test_permissions.PermissionsViewTests.test_plan_edit_athlete_workout + - test_permissions.PermissionsViewTests.test_basic_edit_athelte_workout - Anonymous users can view team members' workout (but not see list of workouts) + - test_aworkouts - Rules for Workouts are transferred to objects related to the Workout, - Charts @@ -205,8 +298,9 @@ def can_view_workout(user,workout): return True return False +can_change_workout = is_workout_user -rules.add_perm('workout.change_workout',is_workout_user) # replaces checkworkoutuser +rules.add_perm('workout.change_workout',can_change_workout) # replaces checkworkoutuser rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutuserview @@ -218,13 +312,22 @@ rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutu """ - These rules apply to planning - Free coach can create planned sessions and team planned sessions + - test_permissions.PermissionsViewTests.test_coach_create_session - Self coach and higher can create planned sessions and team planned sessions + - test_permissions.PermissionsFreeCoach.test_plan_create_session + - test_permissions.PermissionsFreeCoach.test_coach_create_session - Coach can create planned sessions and team planned sessions + - test_permissions.PermissionsFreeCoach.test_plan_create_session + - test_permissions.PermissionsFreeCoach.test_coach_create_session - Self Coach and higher can create planned sessions and team planned sessions + - test_permissions.PermissionsViewTests.test_plan_create_session - Pro or Basic cannot create planned sessions or team planned sessions + - test_permissions.PermissionsFreeCoach.test_pro_create_plannedsession + - test_permissions.PermissionsFreeCoach.test_basic_create_plannedsession - WHO can comment (plannedsession_comment_view) - Only Session manager can change session - Strict View rules (stricter than workouts) + - Basic user cannot manage a training plan - TrainingTarget - rules for view, add, change, delete @@ -299,9 +402,12 @@ def can_delete_plan(user,plan): return False return user == plan.manager.user + + rules.add_perm('plan.view_plan',can_view_plan) rules.add_perm('plan.change_plan',can_change_plan) rules.add_perm('plan.delete_plan',can_delete_plan) +rules.add_perm('plan.can_add_plan',can_add_plan) @rules.predicate def can_view_cycle(user,cycle): @@ -383,6 +489,9 @@ def can_delete_session(user,session): return False + + +rules.add_perm('plannedsession.add_session',can_add_session) rules.add_perm('plannedsession.view_session',can_view_session) rules.add_perm('plannedsession.change_session',can_change_session) rules.add_perm('plannedsession.delete_session',can_delete_session) @@ -395,13 +504,21 @@ rules.add_perm('plannedsession.delete_session',can_delete_session) - team.private - These rules apply to a team - - A Pro rower can be manager of only 1 team (as manager) + - A Pro or Plan rower can be manager of only 1 team + - test_permissions.PermissionsFreeCoach.test_pro_groupmanager + - test_permissions.PermissionsViewTests.test_pro_groups_create - A coach can have any number of teams (as manager) + - test_permissions.PermissionsBasicTest.test_coach_groupmanager - Basic user cannot manage a group + - test_permissions.PermissionsFreeCoach.test_basic_groupmanager - Basic user cannot join groups led by Free Coach or Pro + - test_permissions.PermissionFreeCoach.test_add_basic_pro_or_plan - Basic athletes can be member of Coach led group - - Plan user can create 1 group (as manager) and not more + - test_permissions.PermissionsBasicTest.test_add_coach - Pro users (and higher) can join team led by other Pro (or higher) user + - test_permissions.PermissionsFreeCoach.test_add_proplan_pro_or_plan + - test_permissions.PermissionsViewTests.test_team_member_request_pro_pro + - test_permissions.PermissionsViewTests.test_team_member_request_basic_pro - On downgrade, Coach users lose all but their oldest team (add test!) """ @@ -437,6 +554,10 @@ def can_change_team(user,team): def can_delete_team(user,team): return is_team_manager(user,team) +@rules.predicate +def can_join_team(user,team): + return is_paid_coach(team.manager) or ispromember(user) + # For Team functionality rules.add_perm('teams.view_team',can_view_team) rules.add_perm('teams.add_team',user_is_not_basic) diff --git a/rowers/teams.py b/rowers/teams.py index 5e3fe126..9aa98b68 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -169,15 +169,7 @@ def rower_get_managers(rower): return managers def rower_get_coaches(rower): - coaches = [] - for group in rower.coachinggroups.all(): - try: - coach = Rower.objects.get(mycoachgroup=group) - coaches.append(coach) - except Rower.DoesNotExist: - pass - - return coaches + return rower.get_coaches() def coach_getcoachees(coach): diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index 1671d82d..fae37d38 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -694,7 +694,7 @@ class PermissionsViewTests(TestCase): self.assertEqual(response.status_code,403) # 3, Export Settings - def test_coach_edit_athlete_settings(self): + def test_coach_edit_athlete_exportsettings(self): self.rbasic.team.add(self.teamcoach) self.rbasic.coachinggroups.add(self.coachinggroup) @@ -706,7 +706,7 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) self.assertEqual(response.status_code,200) - def test_coach_edit_athlete_settings_not(self): + def test_coach_edit_athlete_exportsettings_not(self): self.rbasic.team.add(self.teamcoach) login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) @@ -718,7 +718,7 @@ class PermissionsViewTests(TestCase): self.assertEqual(response.status_code,403) # 4, Account settings - def test_coach_edit_athlete_settings(self): + def test_coach_edit_athlete_account(self): self.rbasic.team.add(self.teamcoach) self.rbasic.coachinggroups.add(self.coachinggroup) @@ -730,7 +730,7 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) self.assertEqual(response.status_code,200) - def test_coach_edit_athlete_settings_not(self): + def test_coach_edit_athlete_account_not(self): self.rbasic.team.add(self.teamcoach) login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 097af6f2..74fed42a 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1906,7 +1906,7 @@ class PlannedSessionDelete(DeleteView): return obj -@user_passes_test(isplanmember,login_url="/rowers/paidplans", +@user_passes_test(can_add_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): diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 43fa3315..501e37b2 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -43,7 +43,8 @@ from rowers.rower_rules import ( 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_view_cycle,can_change_cycle,can_delete_cycle, + can_add_workout_member, ) from django.shortcuts import render diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index a8628322..a187f020 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4875,7 +4875,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(user,u.rower): r = getrower(u) else: message = 'Please select a rower' From 88703bb34ee1f26c32d7532c2bf09d97c0783e14 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 15 Jan 2020 11:08:32 +0100 Subject: [PATCH 26/80] some fixes --- rowers/models.py | 12 ++++++------ rowers/urls.py | 2 +- rowers/views/planviews.py | 9 +++++---- rowers/views/statements.py | 5 +++++ 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/rowers/models.py b/rowers/models.py index e2083164..91abb9b4 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -904,11 +904,11 @@ class Rower(models.Model): def get_coaches(self): coaches = [] for group in self.coachinggroups.all(): - try: - coach = Rower.objects.get(mycoachgroup=group) - coaches.append(coach) - except Rower.DoesNotExist: - pass + try: + coach = Rower.objects.get(mycoachgroup=group) + coaches.append(coach) + except Rower.DoesNotExist: + pass return coaches @@ -2665,7 +2665,7 @@ class Workout(models.Model): def save(self, *args, **kwargs): user = self.user - if not can_add_workout(self.user): + if not can_add_workout(user.user): raise forms.ValidationError("Free Coach User cannot have any workouts") super(Workout, self).save(*args, **kwargs) diff --git a/rowers/urls.py b/rowers/urls.py index 4a890095..50b095a1 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -572,7 +572,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( diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 74fed42a..3960de10 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1906,11 +1906,12 @@ class PlannedSessionDelete(DeleteView): return obj -@user_passes_test(can_add_plan,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 rower_create_trainingplan(request,userid=0): - therower = getrequestrower(request,userid=userid) +@permission_required('rower.add_plan',fn=get_rower_by_userid,raise_exception=True) +def rower_create_trainingplan(request,id=0): + therower = getrequestrower(request,userid=id) theuser = therower.user themanager = getrower(request.user) @@ -2016,7 +2017,7 @@ def rower_create_trainingplan(request,userid=0): }, { 'url':reverse(rower_create_trainingplan, - kwargs={'userid':userid}), + kwargs={'id':id}), 'name': 'Manage Plans and Targets' } ] diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 501e37b2..aa984e21 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -348,6 +348,11 @@ def get_user_by_id(*args,**kwargs): return get_object_or_404(User,pk=id) +def get_rower_by_userid(*args,**kwargs): + userid = kwargs['id'] + u = User.objects.get(id=userid) + return u.rower + def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): userid = int(userid) From 8683d8eaa4c979ff1473d67416f921adf908d5f8 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 15 Jan 2020 15:55:41 +0100 Subject: [PATCH 27/80] more fixes --- rowers/models.py | 2 +- rowers/rower_rules.py | 7 +++++-- rowers/teams.py | 9 ++++++--- rowers/templates/menu_other.html | 6 ++---- rowers/templates/menu_profile.html | 4 ++-- rowers/views/planviews.py | 6 +++--- rowers/views/statements.py | 26 +++++--------------------- rowers/views/teamviews.py | 7 +------ rowers/views/workoutviews.py | 4 +++- templates/newbase.html | 2 +- 10 files changed, 29 insertions(+), 44 deletions(-) diff --git a/rowers/models.py b/rowers/models.py index 91abb9b4..6590aa57 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -1351,7 +1351,7 @@ class TrainingPlan(models.Model): def save(self, *args, **kwargs): manager = self.manager - if not can_add_plan(manager): + if not can_add_plan(manager.user): raise ValidationError( "Basic user cannot have a training plan" ) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index c4e58792..5de0d339 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -86,7 +86,7 @@ def user_is_not_basic(user): def is_coach(user): return user.rower.rowerplan in ['coach','freecoach'] -def is_paidcoach(user): +def is_paid_coach(user): return user.rower.rowerplan == 'coach' @rules.predicate @@ -228,6 +228,9 @@ def can_add_workout_member(user,rower): # check if user can plan for the rower @rules.predicate def can_plan_user(user,rower): + if not isplanmember(user): + return False + try: r = user.rower except AttributeError: @@ -243,7 +246,7 @@ def can_plan_user(user,rower): return False -rules.add_perm('rower.can_plan',can_plan_user) # replaces checkaccessplanuser +rules.add_perm('rower.add_plan',can_plan_user) # replaces checkaccessplanuser rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser rules.add_perm('rower.is_pro',ispromember) diff --git a/rowers/teams.py b/rowers/teams.py index 9aa98b68..aa1c8903 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -173,12 +173,15 @@ def rower_get_coaches(rower): def coach_getcoachees(coach): + rowers = [] if coach.mycoachgroup and is_coach(coach.user): - return Rower.objects.filter( + rs = Rower.objects.filter( coachinggroups__in=[coach.mycoachgroup] ).distinct().order_by("user__last_name","user__first_name") - else: - return [] + for r in rs: + rowers.append(r) + + return rowers def coach_remove_athlete(coach,rower): try: diff --git a/rowers/templates/menu_other.html b/rowers/templates/menu_other.html index fad56ed0..a3961006 100644 --- a/rowers/templates/menu_other.html +++ b/rowers/templates/menu_other.html @@ -14,7 +14,7 @@ {% else %}  Compare {% endif %} - +
  •  Upload
  • @@ -33,7 +33,7 @@
  • - + - - {% include 'menuscript.html' %} diff --git a/rowers/templates/menu_profile.html b/rowers/templates/menu_profile.html index bdeb6f54..05cf98f1 100644 --- a/rowers/templates/menu_profile.html +++ b/rowers/templates/menu_profile.html @@ -44,13 +44,13 @@ {% if user.is_authenticated and user|is_manager and rower %}

     

    -{% if user|coach_rowers %} +{% if request.user|coach_rowers %}
      • - {% for member in user|coach_rowers %} + {% for member in request.user|coach_rowers %} {% if member.user == rower.user %} diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 3960de10..5db07f62 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1909,7 +1909,6 @@ class PlannedSessionDelete(DeleteView): @user_passes_test(isplanmember,login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -@permission_required('rower.add_plan',fn=get_rower_by_userid,raise_exception=True) def rower_create_trainingplan(request,id=0): therower = getrequestrower(request,userid=id) theuser = therower.user @@ -1977,7 +1976,8 @@ def rower_create_trainingplan(request,id=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( @@ -2012,7 +2012,7 @@ def rower_create_trainingplan(request,id=0): breadcrumbs = [ { 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + kwargs={'userid':id}), 'name': 'Plan' }, { diff --git a/rowers/views/statements.py b/rowers/views/statements.py index aa984e21..25ba6b3b 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -44,7 +44,7 @@ from rowers.rower_rules import ( 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_add_workout_member,can_plan_user,is_paid_coach ) from django.shortcuts import render @@ -348,9 +348,8 @@ def get_user_by_id(*args,**kwargs): return get_object_or_404(User,pk=id) -def get_rower_by_userid(*args,**kwargs): - userid = kwargs['id'] - u = User.objects.get(id=userid) +def get_rower_by_userid(request,id): + u = User.objects.get(id=id) return u.rower def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): @@ -381,6 +380,7 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): raise Http404("Rower doesn't exist") if userid != 0 and not is_coach_user(request.user,u): + request.session['rowerid'] = request.user.rower.id raise PermissionDenied("You have no access to this user") if notpermanent == False: @@ -414,6 +414,7 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False): raise Http404("Rower doesn't exist") if not is_coach_user(request.user,r.user): + request.session['rowerid'] = r.id raise PermissionDenied("You have no access to this user") if notpermanent == False: @@ -1038,23 +1039,6 @@ 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 - from rowers.utils import ProcessorCustomerError diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 0667b472..4997483e 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -648,15 +648,10 @@ def team_edit_view(request, team_id=0): 'team':t, }) -#@user_passes_test(cancreateteam,login_url="/rowers/paidplans",redirect_field_name=None) +@user_passes_test(can_add_team,login_url="/rowers/paidplans",redirect_field_name=None) def team_create_view(request): r = getrequestrower(request) - if not user_is_not_basic(request.user): - messages.error(request,"You must upgrade to Pro or higher to create teams/training groups") - url = reverse('paidplans') - return HttpResponseRedirect(url) - if request.method == 'POST': teamcreateform = TeamForm(request.POST) if teamcreateform.is_valid(): diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index a187f020..bc3c3838 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -1760,7 +1760,9 @@ def workouts_view(request,message='',successmessage='', # check if access is allowed if not is_rower_team_member(request.user,r): - raise PermissionDenied("Access denied") + request.session['rowerid'] = request.user.rower.id + + raise PermissionDenied("Access denied") diff --git a/templates/newbase.html b/templates/newbase.html index 5fab2852..02d3d0d4 100644 --- a/templates/newbase.html +++ b/templates/newbase.html @@ -246,7 +246,7 @@ {% endif %} {% endif %} {% if user.rower.protrialexpires and user.rower.protrialexpires|is_future_date %} - {% if user.rower.plantrialexpires and user.rower.rowerplan != 'plan' %} + {% if user.rower.plantrialexpires and user.rower.plantrialexpires|is_future_date and user.rower.rowerplan != 'plan' %}
      • {{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial - Would you like to upgrade now? From 86869f595ea7c3c4a9964fecd8ab627ba24b5540 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 15 Jan 2020 20:50:16 +0100 Subject: [PATCH 28/80] making pass tests --- rowers/models.py | 2 +- rowers/rower_rules.py | 2 +- rowers/tests/test_permissions.py | 21 +++++++++++---------- rowers/views/workoutviews.py | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/rowers/models.py b/rowers/models.py index 6590aa57..fd3d419a 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -949,7 +949,7 @@ def check_teams_on_change(sender, **kwargs): #if instance.protrialexpires < datetime.date.today() and instance.plantrialexpires < datetime.date.today(): for id in pk_set: team = Team.objects.get(id=id) - if not can_join_team(instance,team): + if not can_join_team(instance.user,team): raise ValidationError( "You cannot join a team led by a Pro, Free Coach Plan or Self-Coach user" ) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 5de0d339..ecd87d5b 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -124,7 +124,7 @@ def is_protrial(user): ispromember = is_promember | is_protrial -can_have_teams = ispromember +can_have_teams = ispromember | is_coach @rules.predicate def can_add_team(user): diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index fae37d38..b638c9d7 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -1112,21 +1112,22 @@ class PermissionsViewTests(TestCase): ## Pro cannot run analytics on behalf of athlete @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats) def test_pro_edit_athlete_analysis(self,mocked_df): - self.rpro.team.add(self.teampro) + with transaction.atomic(): + self.rpro.team.add(self.teampro) - login = self.c.login(username=self.upro2.username, password=self.upro2password) - self.assertTrue(login) + login = self.c.login(username=self.upro2.username, password=self.upro2password) + self.assertTrue(login) - url = reverse('cumstats', - kwargs={ - 'theuser':self.ubasic.id, - } - ) + url = reverse('cumstats', + kwargs={ + 'theuser':self.ubasic.id, + } + ) - response = self.c.get(url) + response = self.c.get(url) - self.assertEqual(response.status_code,403) + self.assertEqual(response.status_code,403) ## Self Coach cannot upload on behalf of athlete @patch('rowers.dataprep.create_engine') diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index bc3c3838..7d6d681e 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4877,7 +4877,7 @@ def team_workout_upload_view(request,message="", workouttype = form.cleaned_data['workouttype'] if rowerform.is_valid(): u = rowerform.cleaned_data['user'] - if can_add_workout_member(user,u.rower): + if can_add_workout_member(request.user,u.rower): r = getrower(u) else: message = 'Please select a rower' From 7a643d5e018765fe80b5b836d3371f3110dd2750 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 15 Jan 2020 20:57:55 +0100 Subject: [PATCH 29/80] finish test_permissions - pass --- rowers/rower_rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index ecd87d5b..40f2bff1 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -169,7 +169,7 @@ isplanmember = is_planmember | is_plantrial @rules.predicate def can_add_session(user): - return isplanmember(user) + return isplanmember(user) or is_coach(user) # User / Coach relationships (Rower object) From ec8a7cfdd1402287576b16dd63de7ff64d95cf9e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 15 Jan 2020 21:24:15 +0100 Subject: [PATCH 30/80] checking and fixing some rules based redirects --- rowers/rower_rules.py | 14 ++++++++++++++ rowers/views/teamviews.py | 3 ++- rowers/views/userviews.py | 4 ++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 40f2bff1..cb906bfd 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -82,6 +82,20 @@ def user_is_not_basic(user): return False +@rules.predicate +def can_start_trial(user): + if user.is_anonymous: + return False + + return user.rower.protrialexpires > datetime.date(1970,1,1) + +@rules.predicate +def can_start_plantrial(user): + if user.is_anonymous: + return False + + return user.rower.plantrialexpires > datetime.date(1970,1,1) + @rules.predicate def is_coach(user): return user.rower.rowerplan in ['coach','freecoach'] diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 4997483e..93245597 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -437,7 +437,8 @@ def team_requestmembership_view(request,teamid,userid): r = getrequestrower(request,userid=userid) - if t.manager.rower.rowerplan in ['plan','pro'] and r.rowerplan == 'basic': + #if t.manager.rower.rowerplan in ['plan','pro'] and r.rowerplan == 'basic': + if not can_join_team(r.user,t) messages.error(request, "You have to be on a paid plan (Pro or higher) to join this team. As a basic user you can only join teams managed by users on the Coach plan.") diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index b9e89509..d3f4903d 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -9,7 +9,7 @@ from rowers.views.statements import * def start_trial_view(request): r = getrower(request.user) - if r.protrialexpires > 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) @@ -43,7 +43,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) From 4ad334dce7d4307c466ae920bc58d2bc6900ae12 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 15 Jan 2020 21:36:27 +0100 Subject: [PATCH 31/80] further testing passing --- rowers/rower_rules.py | 5 +++-- rowers/tests/test_basicrower.py | 2 ++ rowers/views/statements.py | 3 ++- rowers/views/teamviews.py | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index cb906bfd..586957d1 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -87,14 +87,15 @@ def can_start_trial(user): if user.is_anonymous: return False - return user.rower.protrialexpires > datetime.date(1970,1,1) + + return user.rower.protrialexpires == datetime.date(1970,1,1) @rules.predicate def can_start_plantrial(user): if user.is_anonymous: return False - return user.rower.plantrialexpires > datetime.date(1970,1,1) + return user.rower.plantrialexpires == datetime.date(1970,1,1) @rules.predicate def is_coach(user): diff --git a/rowers/tests/test_basicrower.py b/rowers/tests/test_basicrower.py index 268ab876..79cae214 100644 --- a/rowers/tests/test_basicrower.py +++ b/rowers/tests/test_basicrower.py @@ -20,6 +20,8 @@ class SimpleViewTest(TestCase): birthdate=faker.profile()['birthdate'], gdproptin=True,surveydone=True, gdproptindate=timezone.now(), + protrialexpires=datetime.date(1970,1,1), + plantrialexpires=datetime.date(1970,1,1), rowerplan='basic') self.c = Client() diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 25ba6b3b..1e0f8639 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -44,7 +44,8 @@ from rowers.rower_rules import ( 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_add_workout_member,can_plan_user,is_paid_coach, + can_start_trial, can_start_plantrial ) from django.shortcuts import render diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 93245597..a97c1ce6 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -438,7 +438,7 @@ def team_requestmembership_view(request,teamid,userid): r = getrequestrower(request,userid=userid) #if t.manager.rower.rowerplan in ['plan','pro'] and r.rowerplan == 'basic': - if not can_join_team(r.user,t) + if not can_join_team(r.user,t): messages.error(request, "You have to be on a paid plan (Pro or higher) to join this team. As a basic user you can only join teams managed by users on the Coach plan.") From 94b5e59100c3e0db210ec48343103cb305360d0f Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 15 Jan 2020 21:55:43 +0100 Subject: [PATCH 32/80] annotating _rules with untested --- rowers/rower_rules.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 586957d1..27b81814 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -72,6 +72,7 @@ USER permissions """ +# not tested @rules.predicate def user_is_not_basic(user): if user.rower.rowerplan != 'basic': @@ -188,6 +189,7 @@ def can_add_session(user): # User / Coach relationships (Rower object) +# not tested @rules.predicate def can_plan(user): return user.rower.rowerplan in ['plan','coach','freecoach'] @@ -219,6 +221,7 @@ def is_rower_team_member(user,rower): if user.rower == rower: return True + # below not tested teams = user.rower.team.all() for team in teams: @@ -238,6 +241,7 @@ def can_add_workout_member(user,rower): return False if user == rower.user: return True + # only below tested - need test user == rower.user return isplanmember(user) and user.rower in rower.get_coaches() # check if user can plan for the rower @@ -254,7 +258,7 @@ def can_plan_user(user,rower): if rower == r: return True - # below + # below not tested team_managers = [t.manager for t in rower.team.all() and can_plan(t.manager)] if user_is_not_basic(user): return user in team_managers @@ -314,7 +318,8 @@ def is_workout_user(user,workout): def can_view_workout(user,workout): if workout.privacy != 'private': return True - return False + # below not tested + return user.rower == workout.user can_change_workout = is_workout_user @@ -362,6 +367,8 @@ rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutu """ # Training Target rules + +# untested can_view_target to can_delete_target @rules.predicate def can_view_target(user,target): if user.is_anonymous: @@ -401,6 +408,7 @@ def can_view_plan(user,plan): return True # a plan's coach can view as well + # below untested if is_coach_user(user,plan.manager.user): return True @@ -414,6 +422,7 @@ def can_change_plan(user,plan): return False return user == plan.manager.user +# below untested @rules.predicate def can_delete_plan(user,plan): if user.is_anonymous: @@ -427,6 +436,7 @@ rules.add_perm('plan.change_plan',can_change_plan) rules.add_perm('plan.delete_plan',can_delete_plan) rules.add_perm('plan.can_add_plan',can_add_plan) +# untested @rules.predicate def can_view_cycle(user,cycle): try: @@ -471,9 +481,11 @@ def can_view_session(user,session): if user == session.manager: return True # if you're a rower in the session you can view it + # below untested if user.rower in session.rower.all(): return True # coach users can view sessions created by their team members + # below untested if is_coach(user): teams = user.rower.get_managed_teams() teamusers = [member.u for member in t.rower.all()] @@ -546,7 +558,7 @@ rules.add_perm('plannedsession.delete_session',can_delete_session) def is_team_manager(user,team): return team.manager == user -# check is user is member of team +# check is user is member of team - untested @rules.predicate def is_team_member(user,team): members = team.rower.all() @@ -555,13 +567,13 @@ def is_team_member(user,team): # check if user can view team @rules.predicate def can_view_team(user,team): - # user based + # user based - below untested if user.rower.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach': return False # team is public if team.private == 'open': return True - # team is private + # team is private - below untested return is_team_member(user,team) | is_team_manager(user,team) @rules.predicate @@ -599,6 +611,7 @@ def can_change_course(user,course): return course.manager == user.rower +# untested @rules.predicate def can_delete_course(user,course): if user.is_anonymous: From c23fd15b7c9e304bb9ac5f7b02a4827c4a1c4002 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 16 Jan 2020 14:56:52 +0100 Subject: [PATCH 33/80] improved (fixed) rules and two additional tests for permissions --- rowers/models.py | 34 ++++++++++ rowers/rower_rules.py | 39 ++++++----- rowers/tests/test_permissions.py | 111 +++++++++++++++++++++++++++++++ rowers/tests/test_plans.py | 13 +++- 4 files changed, 180 insertions(+), 17 deletions(-) diff --git a/rowers/models.py b/rowers/models.py index fd3d419a..d06ab70e 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -941,6 +941,7 @@ class CoachOffer(models.Model): from django.db.models.signals import m2m_changed + def check_teams_on_change(sender, **kwargs): instance = kwargs.pop('instance', None) action = kwargs.pop('action', None) @@ -1282,6 +1283,18 @@ class TrainingTarget(models.Model): return stri +def check_trainingtarget_on_change(sender,**kwargs): + instance = kwargs.pop('instance',None) + action = kwargs.pop('action',None) + pk_set = kwargs.pop('pk_set',None) + if action == 'pre_add': + for id in pk_set: + rower = Rower.objects.get(id=id) + if not can_plan_user(instance.manager.user,rower): + raise ValidationError("You cannot add this rower. Not your coachee") + +m2m_changed.connect(check_trainingtarget_on_change, sender=TrainingTarget.rowers.through) + class TrainingTargetForm(ModelForm): class Meta: model = TrainingTarget @@ -1356,6 +1369,8 @@ class TrainingPlan(models.Model): "Basic user cannot have a training plan" ) + + if self.enddate < self.startdate: startdate = self.startdate enddate = self.enddate @@ -1365,6 +1380,13 @@ class TrainingPlan(models.Model): if not self.enddate <= self.startdate: super(TrainingPlan,self).save(*args, **kwargs) + # only add athletes that are allowed to be added + for rower in self.rowers.all(): + if can_plan_user(manager.user,rower): + self.rowers.add(rower) + else: + self.rowers.remove(rower) + if self.status: otherplans = TrainingPlan.objects.filter( status=True).exclude( @@ -1401,6 +1423,18 @@ class TrainingPlan(models.Model): else: createmacrofillers(self) +def check_trainingplan_on_change(sender, **kwargs): + instance = kwargs.pop('instance',None) + action = kwargs.pop('action',None) + pk_set = kwargs.pop('pk_set',None) + if action == 'pre_add': + for id in pk_set: + rower = Rower.objects.get(id=id) + if not can_plan_user(instance.manager.user,rower): + raise ValidationError("You cannot add this rower. Not your coachee") + +m2m_changed.connect(check_trainingplan_on_change, sender=TrainingPlan.rowers.through) + class TrainingPlanForm(ModelForm): class Meta: diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 27b81814..f8edd93f 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -72,7 +72,7 @@ USER permissions """ -# not tested +# used in can_plan_user @rules.predicate def user_is_not_basic(user): if user.rower.rowerplan != 'basic': @@ -156,7 +156,7 @@ def can_add_team(user): @rules.predicate def can_add_plan(user): - return isplanmember(user) + return isplanmember(user) or is_coach(user) @rules.predicate def can_add_workout(user): @@ -189,7 +189,6 @@ def can_add_session(user): # User / Coach relationships (Rower object) -# not tested @rules.predicate def can_plan(user): return user.rower.rowerplan in ['plan','coach','freecoach'] @@ -197,6 +196,9 @@ def can_plan(user): # checks if rower is coach of user @rules.predicate def is_coach_user(usercoach,userrower): + if not is_coach(usercoach): + return False + if usercoach == userrower: return True @@ -221,8 +223,7 @@ def is_rower_team_member(user,rower): if user.rower == rower: return True - # below not tested - teams = user.rower.team.all() + teams = rower.team.all() for team in teams: if team.private == 'open': @@ -247,22 +248,30 @@ def can_add_workout_member(user,rower): # check if user can plan for the rower @rules.predicate def can_plan_user(user,rower): - if not isplanmember(user): + # user must have planning permission + if not can_plan(user): return False - try: - r = user.rower - except AttributeError: - return False - - if rower == r: + # if has planning permission, can always plan for himself + if rower == user.rower: return True - # below not tested - team_managers = [t.manager for t in rower.team.all() and can_plan(t.manager)] - if user_is_not_basic(user): + teams = user.rower.get_managed_teams() + # free coach, plan etc cannot plan for basic + if not is_paid_coach(user) and user_is_not_basic(user): + for t in teams: + if rower in t.rower.all(): + return True + + return user in team_managers + # paying coach can plan for all kinds of rowers + if is_paid_coach(user): + for t in teams: + if rower in t.rower.all(): + return True + return False rules.add_perm('rower.add_plan',can_plan_user) # replaces checkaccessplanuser diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index b638c9d7..fafe1e8b 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -15,6 +15,86 @@ import rowers.teams as teams import rowers.plannedsessions as plannedsessions from rowers.rower_rules import is_coach_user +@override_settings(TESTING=True) +class PlanningPermissionsCoach(TestCase): + def setUp(self): + self.c = Client() + + self.ucoach = UserFactory() + self.rcoach = Rower.objects.create( + user=self.ucoach, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True, + gdproptindate=timezone.now(), + rowerplan='coach', + clubsize=10, + ) + self.ucoachpassword = faker.word() + self.ucoach.set_password(self.ucoachpassword) + self.ucoach.save() + + self.ucoachee = UserFactory() + self.rcoachee = Rower.objects.create( + user=self.ucoachee, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True, + gdproptindate=timezone.now(), + rowerplan='basic' + ) + + self.coachgroup = CoachingGroup.objects.create(name=self.ucoach.first_name) + self.rcoach.mycoachgroup = self.coachgroup + self.rcoach.save() + + self.team = Team.objects.create( + name=faker.word()+'3', + notes=faker.text(), + manager=self.ucoach) + + # coachee is in coach's team + self.rcoachee.team.add(self.team) + + # coachee is coached by coach + self.rcoachee.coachinggroups.add(self.rcoach.mycoachgroup) + self.rcoachee.save() + + self.ugroupmember = UserFactory() + self.rgroupmember = Rower.objects.create( + user=self.ugroupmember, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True, + gdproptindate=timezone.now(), + rowerplan='basic' + ) + + # groupmember is in coach's team (but not coached) + self.rgroupmember.team.add(self.team) + + + # def test_target(self): + + def test_trainingplan(self): + plan = TrainingPlan( + name='test plan', + manager=self.ucoach.rower, + ) + plan.save() + + # can add self and coachee + plan.rowers.add(self.ucoach.rower) + plan.rowers.add(self.ucoachee.rower) + + # test 1 + athletes = plan.rowers.all() + self.assertTrue(self.ucoach.rower in athletes) + self.assertTrue(self.ucoachee.rower in athletes) + + # can add groupmember if not basic + plan.rowers.add(self.rgroupmember) + athletes = plan.rowers.all() + self.assertTrue(self.rgroupmember in athletes) + + @override_settings(TESTING=True) class PermissionsFreeCoach(TestCase): def setUp(self): @@ -884,6 +964,37 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) self.assertEqual(response.status_code,403) + ## Coach can see list of workouts of athlete + def test_coach_athlete_workout_list(self): + self.rbasic.team.add(self.teamcoach) + print(self.rbasic.team.all()) + print(self.teamcoach) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('workouts_view', + kwargs={'rowerid':self.rbasic.id}) + + + + response = self.c.get(url) + print(url,response.status_code) + self.assertEqual(response.status_code,200) + + url = reverse('workouts_view', + kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + print(url,response.status_code) + self.assertEqual(response.status_code,200) + + url = reverse('workouts_view') + + response = self.c.get(url) + print(url,response.status_code) + self.assertEqual(response.status_code,200) + ## Self coach can create one group ## Self coach cannot create more than one group def test_plan_groups_create(self): diff --git a/rowers/tests/test_plans.py b/rowers/tests/test_plans.py index 613478da..b593a8d7 100644 --- a/rowers/tests/test_plans.py +++ b/rowers/tests/test_plans.py @@ -5,7 +5,17 @@ from __future__ import unicode_literals #from __future__ import print_function from .statements import * -nu = datetime.datetime.now() +nu = datetime.datetime.now()self.ucoach = UserFactory() +self.rcoach = Rower.objects.create( + user=self.ucoach, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True, + gdproptindate=timezone.now(), + rowerplan='coach' +) +self.ucoachpassword = faker.word() +self.uchoach.set_password(self.ucoachpassword) +self.ucoach.save() from rowers.utils import allmonths,allsundays import rowers.plannedsessions as plannedsessions @@ -125,7 +135,6 @@ class TrainingPlanTest(TestCase): for url in urls: if 'planbyweeks' in url and not tested: - print(url) response = self.c.get(url,follow=True) self.assertEqual(response.status_code,200) tested = True From 7c167767a26e5fd0eabe0ab098a2796456c7968e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 16 Jan 2020 16:16:44 +0100 Subject: [PATCH 34/80] more fixes --- rowers/rower_rules.py | 3 --- rowers/tests/test_permissions.py | 5 ----- rowers/views/planviews.py | 35 +++++++++++++++----------------- rowers/views/statements.py | 15 +++++++++----- rowers/views/workoutviews.py | 1 - 5 files changed, 26 insertions(+), 33 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index f8edd93f..795bdf21 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -263,9 +263,6 @@ def can_plan_user(user,rower): if rower in t.rower.all(): return True - - return user in team_managers - # paying coach can plan for all kinds of rowers if is_paid_coach(user): for t in teams: diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index fafe1e8b..0f013d70 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -967,8 +967,6 @@ class PermissionsViewTests(TestCase): ## Coach can see list of workouts of athlete def test_coach_athlete_workout_list(self): self.rbasic.team.add(self.teamcoach) - print(self.rbasic.team.all()) - print(self.teamcoach) login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) @@ -979,20 +977,17 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) - print(url,response.status_code) self.assertEqual(response.status_code,200) url = reverse('workouts_view', kwargs={'userid':self.ubasic.id}) response = self.c.get(url) - print(url,response.status_code) self.assertEqual(response.status_code,200) url = reverse('workouts_view') response = self.c.get(url) - print(url,response.status_code) self.assertEqual(response.status_code,200) ## Self coach can create one group diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 5db07f62..c36af4ec 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -155,7 +155,7 @@ def plannedsession_comment_view(request,id=0,userid=0): }) # Cloning sessions -@user_passes_test(isplanmember,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( @@ -306,7 +306,7 @@ def plannedsession_multiclone_view( ) # Individual user creates training for himself -@user_passes_test(isplanmember,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, @@ -316,9 +316,6 @@ def plannedsession_create_view(request, r = getrequestplanrower(request,userid=userid) - - - startdate,enddate = get_dates_timeperiod(request,startdatestring=startdatestring, enddatestring=enddatestring) @@ -461,7 +458,7 @@ def plannedsession_create_view(request, 'timeperiod':timeperiod, }) -@user_passes_test(isplanmember,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, @@ -597,7 +594,7 @@ def plannedsession_multicreate_view(request, return render(request,'plannedsession_multicreate.html',context) # Manager creates sessions for entire team -@user_passes_test(isplanmember,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, @@ -770,7 +767,7 @@ def plannedsession_teamcreate_view(request, }) # Manager edits sessions for entire team -@user_passes_test(isplanmember,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) @@ -1382,7 +1379,7 @@ def plannedsessions_manage_view(request,userid=0, # Clone an existing planned session # need clarity on cloning behavior time shift @permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) -@user_passes_test(isplanmember,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_clone_view(request,id=0,userid=0): @@ -1445,7 +1442,7 @@ def plannedsession_clone_view(request,id=0,userid=0): # Clone an existing planned session # need clarity on cloning behavior time shift @permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) -@user_passes_test(isplanmember,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_teamclone_view(request,id=0): @@ -1504,7 +1501,7 @@ def plannedsession_teamclone_view(request,id=0): return HttpResponseRedirect(url) @permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) -@user_passes_test(isplanmember, 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_totemplate_view(request,id=0): @@ -1532,7 +1529,7 @@ def plannedsession_totemplate_view(request,id=0): # Edit an existing planned session @permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) -@user_passes_test(isplanmember,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_edit_view(request,id=0,userid=0): @@ -1906,7 +1903,7 @@ class PlannedSessionDelete(DeleteView): return obj -@user_passes_test(isplanmember,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,id=0): @@ -2035,7 +2032,7 @@ def rower_create_trainingplan(request,id=0): 'old_targets':old_targets, }) -@user_passes_test(isplanmember,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) @@ -2049,7 +2046,7 @@ def rower_delete_trainingtarget(request,id=0): return HttpResponseRedirect(url) -@user_passes_test(isplanmember,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) @@ -2256,7 +2253,7 @@ class MacroCycleDelete(DeleteView): return obj -@user_passes_test(isplanmember,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, @@ -2346,7 +2343,7 @@ def rower_trainingplan_execution_view(request, ) -@user_passes_test(isplanmember,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('plan.view_plan',fn=get_plan_by_pk,raise_exception=True) @@ -2756,7 +2753,7 @@ class TrainingTargetUpdate(UpdateView): from rowers.utils import allsundays -@user_passes_test(isplanmember,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) @@ -2806,7 +2803,7 @@ def planmesocyclebyweek(request,id=0,userid=0): from rowers.utils import allmonths -@user_passes_test(isplanmember,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) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 1e0f8639..1ea3b63b 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -45,7 +45,7 @@ from rowers.rower_rules import ( 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_start_trial, can_start_plantrial,can_plan ) from django.shortcuts import render @@ -345,7 +345,10 @@ def get_user_by_id(*args,**kwargs): try: id = args[1] except IndexError: - id = request.user.id + try: + id = kwargs['id'] + except KeyError: + id = request.user.id return get_object_or_404(User,pk=id) @@ -354,7 +357,6 @@ def get_rower_by_userid(request,id): return u.rower def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): - userid = int(userid) rowerid = int(rowerid) @@ -380,7 +382,10 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): except Rower.DoesNotExist: raise Http404("Rower doesn't exist") - if userid != 0 and not is_coach_user(request.user,u): + 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") @@ -414,7 +419,7 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False): except Rower.DoesNotExist: raise Http404("Rower doesn't exist") - if not is_coach_user(request.user,r.user): + if not can_plan_user(request.user,r ): request.session['rowerid'] = r.id raise PermissionDenied("You have no access to this user") diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 7d6d681e..628163b4 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -1757,7 +1757,6 @@ 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 is_rower_team_member(request.user,r): request.session['rowerid'] = request.user.rower.id From 28710dbdd6366076b2f6a8e89811cba0b1dd092a Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 16 Jan 2020 17:07:41 +0100 Subject: [PATCH 35/80] fix --- rowers/views/analysisviews.py | 1 + rowers/views/statements.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index d7792db5..10f5672f 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 diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 1ea3b63b..e1a1f627 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -340,6 +340,14 @@ def get_workout_default_page(request,id): else: return reverse('workout_workflow_view',kwargs={'id':id}) +def get_user_by_userid(*args,**kwargs): + try: + id = kwargs['userid'] + except KeyError: + id = request.user.id + + return get_object_or_404(User,pk=id) + def get_user_by_id(*args,**kwargs): request = args[0] try: From 4aa3649a7cb8b0a10a5c0ded07e4f34ed301b689 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 16 Jan 2020 20:09:32 +0100 Subject: [PATCH 36/80] added permissions in analysis views --- rowers/rower_rules.py | 1 + rowers/tests/test_permissions.py | 22 +++++++++++++++++++++- rowers/tests/test_plans.py | 3 ++- rowers/urls.py | 2 +- rowers/views/analysisviews.py | 21 ++++++++++++++++++--- rowers/views/statements.py | 7 +++++-- 6 files changed, 48 insertions(+), 8 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 795bdf21..2f4c06d1 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -212,6 +212,7 @@ def is_coach_user(usercoach,userrower): coaches.append(coach) for coach in coaches: + print(usercoach.rower,coach) if usercoach.rower == coach: return True diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index 0f013d70..9ceea306 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -841,6 +841,8 @@ class PermissionsViewTests(TestCase): self.assertEqual(response.status_code,200) + + @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats) def test_coach_edit_athlete_analysis_not(self,mocked_df): self.rbasic.team.add(self.teamcoach) @@ -851,7 +853,7 @@ class PermissionsViewTests(TestCase): url = reverse('cumstats', kwargs={ - 'theuser':self.ubasic.id, + 'userid':self.ubasic.id, } ) @@ -859,6 +861,24 @@ class PermissionsViewTests(TestCase): self.assertEqual(response.status_code,403) + @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats) + def test_coach_edit_athlete_analysis_not2(self,mocked_df): + self.rbasic.team.add(self.teamcoach) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + + url = reverse('analysis_new', + kwargs={ + 'userid':self.ubasic.id, + } + ) + + response = self.c.get(url) + + self.assertEqual(response.status_code,403) + ## Coach can upload on behalf of athlete - if team allows @patch('rowers.dataprep.create_engine') diff --git a/rowers/tests/test_plans.py b/rowers/tests/test_plans.py index b593a8d7..e367c509 100644 --- a/rowers/tests/test_plans.py +++ b/rowers/tests/test_plans.py @@ -5,7 +5,8 @@ from __future__ import unicode_literals #from __future__ import print_function from .statements import * -nu = datetime.datetime.now()self.ucoach = UserFactory() +nu = datetime.datetime.now() +self.ucoach = UserFactory() self.rcoach = Rower.objects.create( user=self.ucoach, birthdate=faker.profile()['birthdate'], diff --git a/rowers/urls.py b/rowers/urls.py index 50b095a1..91614718 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -274,7 +274,7 @@ urlpatterns = [ re_path(r'^histodata/$',views.histo_data,name='histo_data'), # re_path(r'^histo/user/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.histo,name='histo'), re_path(r'^histo/$',views.histo,name='histo'), - re_path(r'^cumstats/user/(?P\d+)/$',views.cumstats,name='cumstats'), + re_path(r'^cumstats/user/(?P\d+)/$',views.cumstats,name='cumstats'), # re_path(r'^cumstats/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.cumstats,name='cumstats'), # re_path(r'^cumstats/user/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.cumstats,name='cumstats'), # re_path(r'^cumstats/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.cumstats,name='cumstats'), diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 10f5672f..be1190da 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -670,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') @@ -728,6 +729,7 @@ 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) +@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) def histo(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), @@ -1333,6 +1335,7 @@ def planrequired_view(request): @user_passes_test(isplanmember,login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) +@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) def fitnessmetric_view(request,id=0,mode='rower', startdate=timezone.now()-timezone.timedelta(days=365), enddate=timezone.now()): @@ -2151,6 +2154,7 @@ 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) +@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) def otwrankings_view(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), @@ -2556,6 +2560,7 @@ 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) +@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) def oterankings_view(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), @@ -2907,6 +2912,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="", @@ -3113,6 +3119,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, @@ -3366,6 +3373,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, @@ -3533,6 +3541,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="", @@ -3741,6 +3750,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, @@ -3858,6 +3868,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, @@ -3976,7 +3987,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, @@ -3989,7 +4001,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: @@ -4107,7 +4119,7 @@ def cumstats(request,theuser=0, options = { 'modality': modality, - 'theuser': theuser.id, + 'userid': theuser.id, 'waterboattype':waterboattype, 'startdatestring':startdatestring, 'enddatestring':enddatestring, @@ -4361,6 +4373,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) @@ -4439,6 +4452,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: @@ -4496,6 +4510,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/statements.py b/rowers/views/statements.py index e1a1f627..4fd3128d 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -341,12 +341,15 @@ def get_workout_default_page(request,id): return reverse('workout_workflow_view',kwargs={'id':id}) def get_user_by_userid(*args,**kwargs): + request = args[0] + print(kwargs,request.user.id,'get_user_by_id') try: id = kwargs['userid'] except KeyError: id = request.user.id - return get_object_or_404(User,pk=id) + u = get_object_or_404(User,pk=id) + return u def get_user_by_id(*args,**kwargs): request = args[0] @@ -360,7 +363,7 @@ def get_user_by_id(*args,**kwargs): return get_object_or_404(User,pk=id) -def get_rower_by_userid(request,id): +def get_rower_by_id(request,id): u = User.objects.get(id=id) return u.rower From df290522564efc05bbc89d565e44c03d44a15fd3 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 16 Jan 2020 20:12:33 +0100 Subject: [PATCH 37/80] removing print statements --- rowers/rower_rules.py | 1 - rowers/tests/test_permissions.py | 6 +++--- rowers/views/statements.py | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 2f4c06d1..795bdf21 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -212,7 +212,6 @@ def is_coach_user(usercoach,userrower): coaches.append(coach) for coach in coaches: - print(usercoach.rower,coach) if usercoach.rower == coach: return True diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index 9ceea306..7dbcd4b5 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -833,7 +833,7 @@ class PermissionsViewTests(TestCase): url = reverse('cumstats', kwargs={ - 'theuser':self.ubasic.id, + 'userid':self.ubasic.id, } ) @@ -1124,7 +1124,7 @@ class PermissionsViewTests(TestCase): url = reverse('cumstats', kwargs={ - 'theuser':self.ubasic.id, + 'userid':self.ubasic.id, } ) @@ -1247,7 +1247,7 @@ class PermissionsViewTests(TestCase): url = reverse('cumstats', kwargs={ - 'theuser':self.ubasic.id, + 'userid':self.ubasic.id, } ) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 4fd3128d..c260d3bc 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -342,7 +342,6 @@ def get_workout_default_page(request,id): def get_user_by_userid(*args,**kwargs): request = args[0] - print(kwargs,request.user.id,'get_user_by_id') try: id = kwargs['userid'] except KeyError: From ae366b2f174ab3bbf6a073c869b4e66b07798848 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 16 Jan 2020 20:20:59 +0100 Subject: [PATCH 38/80] typo correc --- rowers/tests/test_plans.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/rowers/tests/test_plans.py b/rowers/tests/test_plans.py index e367c509..cd93bd29 100644 --- a/rowers/tests/test_plans.py +++ b/rowers/tests/test_plans.py @@ -6,17 +6,7 @@ from __future__ import unicode_literals #from __future__ import print_function from .statements import * nu = datetime.datetime.now() -self.ucoach = UserFactory() -self.rcoach = Rower.objects.create( - user=self.ucoach, - birthdate=faker.profile()['birthdate'], - gdproptin=True,surveydone=True, - gdproptindate=timezone.now(), - rowerplan='coach' -) -self.ucoachpassword = faker.word() -self.uchoach.set_password(self.ucoachpassword) -self.ucoach.save() + from rowers.utils import allmonths,allsundays import rowers.plannedsessions as plannedsessions From c186895e7ac4140f19cb50f73a9ae37137953542 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 16 Jan 2020 22:01:54 +0100 Subject: [PATCH 39/80] rules rules rules --- rowers/rower_rules.py | 3 +-- rowers/tests/test_permissions.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 795bdf21..ff9ef370 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -243,7 +243,7 @@ def can_add_workout_member(user,rower): if user == rower.user: return True # only below tested - need test user == rower.user - return isplanmember(user) and user.rower in rower.get_coaches() + return is_coach(user) and user.rower in rower.get_coaches() # check if user can plan for the rower @rules.predicate @@ -324,7 +324,6 @@ def is_workout_user(user,workout): def can_view_workout(user,workout): if workout.privacy != 'private': return True - # below not tested return user.rower == workout.user can_change_workout = is_workout_user diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index 7dbcd4b5..c5576c89 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -609,6 +609,8 @@ class PermissionsViewTests(TestCase): rowerplan='basic') self.ubasic_workouts = WorkoutFactory.create_batch(5, user=self.rbasic) + self.ubasic_workouts[0].privacy == 'private' + self.factory = RequestFactory() self.ubasicpassword = faker.word() self.ubasic.set_password(self.ubasicpassword) @@ -634,6 +636,24 @@ class PermissionsViewTests(TestCase): manager=self.ucoach) + ## only ubasic can view ubasic_workouts[0] which is private + def test_view_workout(self): + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('workout_view', + kwargs={'id':self.ubasic_workouts[0].id}) + response = self.c.get(url) + self.assertTrue(response.status_code,403) + + login = self.c.login(username=self.ubasic.username, password=self.ubasicpassword) + self.assertTrue(login) + + url = reverse('workout_view', + kwargs={'id':self.ubasic_workouts[0].id}) + response = self.c.get(url) + self.assertTrue(response.status_code,200) + ## Coach can have any number of groups def test_coach_groups_create(self): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) From 1c9bdc24b55fb6b9ccb1c9524fee0dea2a70a9c1 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 17 Jan 2020 14:33:56 +0100 Subject: [PATCH 40/80] bug fixing - bugs detected by manually poking around on the site need additional testing suite to systematically go through all permissions need additional permissions check at Model level - models.py --- rowers/c2stuff.py | 4 ++-- rowers/rower_rules.py | 8 ++++---- rowers/stravastuff.py | 4 ++-- rowers/views/planviews.py | 7 ++++--- rowers/views/statements.py | 2 +- rowers/views/workoutviews.py | 12 ++++++------ 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py index 432b5634..83d18ae7 100644 --- a/rowers/c2stuff.py +++ b/rowers/c2stuff.py @@ -14,7 +14,7 @@ import datetime from requests import Request, Session import rowers.mytypes as mytypes from rowers.mytypes import otwtypes -from rowers.rower_rules import is_workout_user +from rowers.rower_rules import is_workout_user,ispromember from iso8601 import ParseError import numpy @@ -156,7 +156,7 @@ def add_stroke_data(user,c2id,workoutid,startdatetime,csvfilename, def get_c2_workouts(rower): - if not isprorower(rower): + if not ispromember(rower.user): return 0 try: diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index ff9ef370..dd78d76b 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -193,15 +193,15 @@ def can_add_session(user): def can_plan(user): return user.rower.rowerplan in ['plan','coach','freecoach'] -# checks if rower is coach of user +# checks if rower is coach of user (or is user himself) @rules.predicate def is_coach_user(usercoach,userrower): - if not is_coach(usercoach): - return False - if usercoach == userrower: return True + if not is_coach(usercoach): + return False + r = userrower.rower coaches = [] diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index 37e33dbc..30e8ab61 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -18,7 +18,7 @@ queuehigh = django_rq.get_queue('low') from rowers.dataprep import columndict -from rowers.rower_rules import is_workout_user +from rowers.rower_rules import is_workout_user,ispromember import stravalib from stravalib.exc import ActivityUploadFailed,TimeoutExceeded @@ -125,7 +125,7 @@ def get_strava_workout_list(user,limit_n=0): # gets all new Strava workouts for a rower def get_strava_workouts(rower): - if not isprorower(rower): + if not ispromember(rower.user): return 0 try: diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index c36af4ec..2548c41f 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -2343,9 +2343,10 @@ def rower_trainingplan_execution_view(request, ) -@user_passes_test(can_plan,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, diff --git a/rowers/views/statements.py b/rowers/views/statements.py index c260d3bc..e6b43cb3 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -429,7 +429,7 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False): except Rower.DoesNotExist: raise Http404("Rower doesn't exist") - if not can_plan_user(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") diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 628163b4..c4ac32c5 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4775,22 +4775,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) From d1256c8c063e578c1a2d0febf88f16aaca2ff824 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 18 Jan 2020 14:38:01 +0100 Subject: [PATCH 41/80] prototype for testing permissions sets --- rowers/tests/test_permissions2.py | 139 ++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 rowers/tests/test_permissions2.py diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py new file mode 100644 index 00000000..64dee035 --- /dev/null +++ b/rowers/tests/test_permissions2.py @@ -0,0 +1,139 @@ +from .statements import * + +@override_settings(TESTING=True) +class PermissionsViewTests(TestCase): + def setUp(self): + self.c = Client() + ## Users - Pro, Basic, Coach & Self Coach + + self.ucoach = UserFactory(username='coachuser') + self.rcoach = Rower.objects.create(user=self.ucoach, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True,gdproptindate=timezone.now(), + rowerplan='coach',clubsize=10) + + self.ucoach_workouts = WorkoutFactory.create_batch(5, user=self.rcoach) + self.coachinggroup = CoachingGroup.objects.create() + self.rcoach.mycoachgroup = self.coachinggroup + self.rcoach.save() + self.factory = RequestFactory() + self.ucoachpassword = faker.word() + self.ucoach.set_password(self.ucoachpassword) + self.ucoach.save() + + self.uplan = UserFactory(username='planuser') + self.rplan = Rower.objects.create(user=self.uplan, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True,gdproptindate=timezone.now(), + rowerplan='plan') + + self.uplan_workouts = WorkoutFactory.create_batch(5, user=self.rplan) + self.factory = RequestFactory() + self.uplanpassword = faker.word() + self.uplan.set_password(self.uplanpassword) + self.uplan.save() + + self.upro = UserFactory(username='prouser') + self.rpro = Rower.objects.create(user=self.upro, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True,gdproptindate=timezone.now(), + rowerplan='pro') + + self.upro_workouts = WorkoutFactory.create_batch(5, user=self.rpro) + self.factory = RequestFactory() + self.upropassword = faker.word() + self.upro.set_password(self.upropassword) + self.upro.save() + + self.uplan2 = UserFactory(username='planuser2') + self.rplan2 = Rower.objects.create(user=self.uplan2, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True,gdproptindate=timezone.now(), + rowerplan='plan') + + self.uplan2_workouts = WorkoutFactory.create_batch(5, user=self.rplan2) + self.factory = RequestFactory() + self.uplan2password = faker.word() + self.uplan2.set_password(self.uplan2password) + self.uplan2.save() + + self.upro2 = UserFactory(username='prouser2') + self.rpro2 = Rower.objects.create(user=self.upro2, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True,gdproptindate=timezone.now(), + rowerplan='pro') + + self.upro2_workouts = WorkoutFactory.create_batch(5, user=self.rpro2) + self.factory = RequestFactory() + self.upro2password = faker.word() + self.upro2.set_password(self.upro2password) + self.upro2.save() + + self.ubasic = UserFactory(username='basicuser') + self.rbasic = Rower.objects.create(user=self.ubasic, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True,gdproptindate=timezone.now(), + rowerplan='basic') + + self.ubasic_workouts = WorkoutFactory.create_batch(5, user=self.rbasic) + self.ubasic_workouts[0].privacy == 'private' + + self.factory = RequestFactory() + self.ubasicpassword = faker.word() + self.ubasic.set_password(self.ubasicpassword) + self.ubasic.save() + + + + ## TeamPro, TeamCoach, TeamSelfCoach + + self.teampro = Team.objects.create( + name=faker.word(), + notes=faker.text(), + manager=self.upro2) + + self.teamplan = Team.objects.create( + name=faker.word()+'2', + notes=faker.text(), + manager=self.uplan2) + + self.teamcoach = Team.objects.create( + name=faker.word()+'3', + notes=faker.text(), + manager=self.ucoach) + + viewstotest = [ + ('workout_view', + [ + { + 'login': False, + 'kwargs': {'id': encoded1} + } + ]) + ] + + + @parameterized.expand(viewstotest) + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.read_df_sql') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('requests.get',side_effect=mocked_requests) + @patch('requests.post',side_effect=mocked_requests) + @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) + def test_permissions_generator( + self,view,permissions, + mocked_sqlalchemy, + mocked_read_df_sql, + mocked_getsmallrowdata_db, + mock_get, + mock_post, + mocked_get_video_data, + ): + + for p in permissions: + url = reverse(view,kwargs=p['kwargs']) + if p['login']: + login = self.c.login(kwargs=p['login']) + + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) From 1e7253995b80b482f0ec5eeac6c17f6828aeae72 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 19 Jan 2020 11:45:41 +0100 Subject: [PATCH 42/80] adding list of views first step to testing permissions --- rowers/tests/test_permissions2.py | 15 +- viewnames.csv | 312 ++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+), 4 deletions(-) create mode 100644 viewnames.csv diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index 64dee035..06313695 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -104,12 +104,19 @@ class PermissionsViewTests(TestCase): viewstotest = [ ('workout_view', - [ { - 'login': False, - 'kwargs': {'id': encoded1} + 'anonymous': True, + 'anonymous_response':200, + 'own': 'basic', + 'own_response': 200, + 'member': 'basic', + 'member_response': 200, + 'coachee': 'coach', + 'coachee_response': 200, + 'workoutid': False, + 'userid': True, } - ]) + ) ] diff --git a/viewnames.csv b/viewnames.csv new file mode 100644 index 00000000..e2047205 --- /dev/null +++ b/viewnames.csv @@ -0,0 +1,312 @@ +,id,view,anonymous,anonymous_response,own,own_response,member,member_response,coachee,coachee_response,staff_only,userid,workoutid +0,0,workouts_summaries_email_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +1,1,rower_update_empower_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +2,2,agegroupcpview,TRUE,200,basic,200,basic,200,coach,200,True,False,False +3,4,ajax_agegrouprecords,TRUE,200,basic,200,basic,200,coach,200,True,False,False +4,5,fitness_metric_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +5,6,agegrouprecordview,TRUE,200,basic,200,basic,200,coach,200,True,False,False +6,7,workouts_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +7,8,virtualevents_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +8,9,virtualevent_create_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +9,10,indoorvirtualevent_create_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +10,11,virtualevent_toggle_email_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +11,12,indoorvirtualevent_toggle_email_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +12,13,virtualevent_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +13,14,virtualevent_ranking_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +14,15,virtualevent_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +15,16,indoorvirtualevent_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +16,17,virtualevent_register_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +17,18,indoorvirtualevent_register_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +18,19,virtualevent_addboat_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +19,20,virtualevent_withdraw_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +20,21,virtualevent_submit_result_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +21,22,virtualevent_disqualify_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +22,24,courses_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +23,25,course_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +24,26,addmanual_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +25,27,team_comparison_select,TRUE,200,basic,200,basic,200,coach,200,True,True,True +26,28,workouts_join_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +27,29,workouts_join_select,TRUE,200,basic,200,basic,200,coach,200,True,True,False +28,30,user_boxplot_select,TRUE,200,basic,200,basic,200,coach,200,True,True,False +29,31,analysis_new,TRUE,200,basic,200,basic,200,coach,200,True,True,False +30,32,user_multiflex_select,TRUE,200,basic,200,basic,200,coach,200,True,True,False +31,33,session_jobs_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +32,34,session_jobs_status,TRUE,200,basic,200,basic,200,coach,200,True,False,False +33,35,kill_async_job,TRUE,200,basic,200,basic,200,coach,200,True,False,False +34,36,post_progress,TRUE,200,basic,200,basic,200,coach,200,True,False,False +35,37,graphs_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +36,38,fitnessmetric_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +37,39,rankings_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +38,40,rankings_view2,TRUE,200,basic,200,basic,200,coach,200,True,False,False +39,41,otwrankings_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +40,42,oterankings_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +41,43,cum_flex,TRUE,200,basic,200,basic,200,coach,200,True,False,False +42,44,analysis_view_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False +43,47,cum_flex_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False +44,48,histo,TRUE,200,basic,200,basic,200,coach,200,True,False,False +45,49,histo_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False +46,51,cumstats,TRUE,200,basic,200,basic,200,coach,200,True,True,False +47,53,graph_show_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +48,54,GraphDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False +49,55,get_thumbnails,TRUE,200,basic,200,basic,200,coach,200,True,False,True +50,56,otw_use_impeller,TRUE,200,basic,200,basic,200,coach,200,True,False,True +51,57,otw_use_gps,TRUE,200,basic,200,basic,200,coach,200,True,False,True +52,58,workout_toggle_ranking,TRUE,200,basic,200,basic,200,coach,200,True,False,True +53,59,team_workout_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +54,60,workout_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +55,61,workout_histo_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +56,62,workout_forcecurve_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +57,63,workout_unsubscribe_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +58,64,workout_comment_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +59,65,workout_tcxemail_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +60,66,workout_gpxemail_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +61,67,workout_csvemail_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +62,68,workout_csvtoadmin_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +63,69,workout_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +64,70,workout_map_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +65,71,workout_update_cp_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +66,72,instroke_chart,TRUE,200,basic,200,basic,200,coach,200,True,False,False +67,73,instroke_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +68,74,workout_stats_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +69,75,workout_data_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +70,76,workout_otwsetpower_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +71,77,workout_otwpowerplot_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +72,78,workout_wind_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +73,79,workout_uploadimage_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +74,80,virtualevent_compare_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +75,81,virtualevent_uploadimage_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +76,82,virtualevent_setlogo_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +77,83,logo_delete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +78,84,workout_downloadwind_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +79,85,workout_downloadmetar_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +80,86,workout_stream_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +81,87,workout_summary_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +82,88,workout_summary_restore_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +83,89,workout_split_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +84,90,workout_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +85,91,workout_video_create_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +86,92,VideoDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False +87,93,workout_video_view_mini,TRUE,200,basic,200,basic,200,coach,200,True,False,True +88,94,workout_video_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +89,95,list_videos,TRUE,200,basic,200,basic,200,coach,200,True,False,False +90,96,video_selectworkout,TRUE,200,basic,200,basic,200,coach,200,True,False,False +91,98,workout_fusion_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +92,99,workout_fusion_list,TRUE,200,basic,200,basic,200,coach,200,True,False,True +93,100,TemplateView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +94,103,workout_add_chart_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +95,112,WorkoutDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False +96,113,workout_smoothenpace_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +97,114,workout_undo_smoothenpace_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +98,115,workout_c2import_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +99,120,workout_stravaimport_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +100,122,workout_getc2workout_all,TRUE,200,basic,200,basic,200,coach,200,True,False,False +101,124,workout_getimportview,TRUE,200,basic,200,basic,200,coach,200,True,False,False +102,125,workout_getstravaworkout_all,TRUE,200,basic,200,basic,200,coach,200,True,False,False +103,126,workout_getstravaworkout_next,TRUE,200,basic,200,basic,200,coach,200,True,False,False +104,127,workout_sporttracksimport_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +105,129,workout_getsporttracksworkout_all,TRUE,200,basic,200,basic,200,coach,200,True,False,False +106,130,workout_polarimport_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +107,132,workout_runkeeperimport_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +108,134,workout_underarmourimport_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +109,135,workout_c2_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +110,136,workout_strava_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +111,137,workout_recalcsummary_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +112,138,workout_sporttracks_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +113,139,workout_runkeeper_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +114,140,workout_underarmour_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +115,141,workout_tp_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +116,142,multi_compare_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +117,145,alerts_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +118,147,AlertDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False +119,148,alert_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +120,150,alert_create_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +121,152,alert_report_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +122,155,boxplot_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +123,157,boxplot_view_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False +124,158,multiflex_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +125,160,multiflex_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False +126,161,deactivate_user,TRUE,200,basic,200,basic,200,coach,200,True,False,False +127,162,remove_user,TRUE,200,basic,200,basic,200,coach,200,True,False,False +128,163,user_gdpr_confirm,TRUE,200,basic,200,basic,200,coach,200,True,False,False +129,165,user_gdpr_optin,TRUE,200,basic,200,basic,200,coach,200,True,False,False +130,167,rower_teams_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +131,168,rower_calcdps_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +132,169,rower_exportsettings_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +133,171,team_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +134,172,team_members_stats_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +135,173,team_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +136,174,team_leaveconfirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +137,175,team_leave_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +138,176,team_deleteconfirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +139,177,team_requestmembership_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +140,178,reject_revoke_coach_request,TRUE,200,basic,200,basic,200,coach,200,True,False,False +141,179,coach_drop_athlete_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +142,180,coach_drop_athlete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +143,181,athlete_drop_coach_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +144,182,athlete_drop_coach_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +145,184,reject_revoke_coach_offer,TRUE,200,basic,200,basic,200,coach,200,True,False,False +146,186,request_coaching_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +147,187,offer_coaching_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +148,188,coach_accept_coachrequest_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +149,189,rower_accept_coachoffer_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +150,190,team_delete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +151,191,team_create_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +152,192,manager_member_drop_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +153,193,invitation_reject_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +154,194,invitation_revoke_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +155,195,rower_invitations_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +156,196,raise_500,TRUE,200,basic,200,basic,200,coach,200,True,False,False +157,198,request_revoke_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +158,199,request_reject_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +159,200,manager_requests_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +160,202,rower_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +161,204,rower_prefs_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +162,205,transactions_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +163,208,rower_c2_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False +164,209,rower_polar_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False +165,210,rower_revokeapp_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +166,211,rower_strava_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False +167,212,rower_sporttracks_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False +168,213,rower_underarmour_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False +169,214,rower_tp_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False +170,215,rower_runkeeper_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False +171,216,rower_sporttracks_token_refresh,TRUE,200,basic,200,basic,200,coach,200,True,False,False +172,217,rower_underarmour_token_refresh,TRUE,200,basic,200,basic,200,coach,200,True,False,False +173,218,rower_tp_token_refresh,TRUE,200,basic,200,basic,200,coach,200,True,False,False +174,219,rower_c2_token_refresh,TRUE,200,basic,200,basic,200,coach,200,True,False,False +175,220,rower_favoritecharts_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +176,222,workout_workflow_config2_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +177,224,workflow_default_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +178,225,sendmail,TRUE,200,basic,200,basic,200,coach,200,True,False,False +179,231,analysis_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +180,232,laboratory_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +181,233,errormessage_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +182,237,payment_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +183,238,upgrade_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +184,240,downgrade_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +185,241,billing_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +186,242,upgrade_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +187,243,downgrade_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +188,244,payment_completed_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +189,245,downgrade_completed_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +190,246,paidplans_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +191,247,plan_stop_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +192,248,plan_tobasic_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +193,249,checkouts_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +194,250,upgrade_checkouts_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +195,251,downgrade_checkouts_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +196,252,planrequired_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +197,253,start_trial_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +198,254,start_plantrial_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +199,256,rower_register_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +200,257,freecoach_register_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +201,259,workout_workflow_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +202,260,workout_flexchart3_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +203,264,rower_process_testcallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False +204,265,rower_create_trainingplan,TRUE,200,basic,200,basic,200,coach,200,True,False,False +205,267,TrainingPlanDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False +206,268,MicroCycleDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False +207,269,MesoCycleDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False +208,270,MacroCycleDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False +209,271,rower_trainingplan_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +210,279,rower_trainingplan_execution_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +211,281,TrainingMacroCycleUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False +212,282,TrainingMesoCycleUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False +213,283,planmacrocyclebymonth,TRUE,200,basic,200,basic,200,coach,200,True,False,False +214,285,planmesocyclebyweek,TRUE,200,basic,200,basic,200,coach,200,True,False,False +215,287,TrainingMicroCycleUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False +216,288,rower_delete_trainingtarget,TRUE,200,basic,200,basic,200,coach,200,True,False,False +217,289,TrainingPlanUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False +218,290,TrainingTargetUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False +219,291,strokedataform,TRUE,200,basic,200,basic,200,coach,200,True,False,False +220,292,plannedsession_teamcreate_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +221,296,plannedsession_teamedit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +222,298,plannedsession_create_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +223,300,plannedsession_multiclone_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +224,302,plannedsession_multicreate_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +225,305,plannedsession_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +226,306,plannedsession_totemplate_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +227,307,plannedsession_compare_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +228,310,plannedsession_clone_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +229,311,plannedsession_teamclone_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +230,313,plannedsession_detach_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +231,315,plannedsession_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +232,317,PlannedSessionDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False +233,319,plannedsessions_manage_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +234,323,plannedsessions_coach_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +235,325,plannedsessions_coach_icsemail_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +236,326,plannedsessions_print_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +237,327,plannedsession_comment_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +238,330,plannedsessions_icsemail_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +239,332,plannedsessions_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False +240,334,course_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +241,335,course_delete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +242,336,course_kmldownload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +243,337,course_replace_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +244,338,course_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +245,339,course_map_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +246,341,c2listdebug_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +247,343,rower_process_callback,TRUE,200,basic,200,basic,200,coach,200,True,False,False +248,344,rower_process_stravacallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False +249,345,rower_process_sporttrackscallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False +250,346,rower_process_underarmourcallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False +251,347,rower_process_polarcallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False +252,348,rower_process_runkeepercallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False +253,349,rower_process_tpcallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False +254,350,rower_process_twittercallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False +255,351,set_language,TRUE,200,basic,200,basic,200,coach,200,True,False,False +256,352,SetOffsetView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +257,353,stats,TRUE,200,basic,200,basic,200,coach,200,True,False,False +258,354,stats_json,TRUE,200,basic,200,basic,200,coach,200,True,False,False +259,355,jobs,TRUE,200,basic,200,basic,200,coach,200,True,False,False +260,356,workers,TRUE,200,basic,200,basic,200,coach,200,True,False,False +261,357,worker_details,TRUE,200,basic,200,basic,200,coach,200,True,False,False +262,358,finished_jobs,TRUE,200,basic,200,basic,200,coach,200,True,False,False +263,359,started_jobs,TRUE,200,basic,200,basic,200,coach,200,True,False,False +264,360,deferred_jobs,TRUE,200,basic,200,basic,200,coach,200,True,False,False +265,361,clear_queue,TRUE,200,basic,200,basic,200,coach,200,True,False,False +266,362,requeue_all,TRUE,200,basic,200,basic,200,coach,200,True,False,False +267,363,job_detail,TRUE,200,basic,200,basic,200,coach,200,True,False,False +268,364,delete_job,TRUE,200,basic,200,basic,200,coach,200,True,False,False +269,365,actions,TRUE,200,basic,200,basic,200,coach,200,True,False,False +270,366,requeue_job_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +271,367,enqueue_job,TRUE,200,basic,200,basic,200,coach,200,True,False,False +272,368,JavaScriptCatalog,TRUE,200,basic,200,basic,200,coach,200,True,False,False +273,369,serve,TRUE,200,basic,200,basic,200,coach,200,True,False,False +274,371,LoginView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +275,372,LogoutView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +276,373,PasswordChangeView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +277,374,PasswordChangeDoneView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +278,375,PasswordResetView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +279,376,PasswordResetDoneView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +280,377,PasswordResetConfirmView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +281,378,PasswordResetCompleteView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +282,386,index,TRUE,200,basic,200,basic,200,coach,200,True,False,False +283,387,login,TRUE,200,basic,200,basic,200,coach,200,True,False,False +284,388,logout,TRUE,200,basic,200,basic,200,coach,200,True,False,False +285,389,password_change,TRUE,200,basic,200,basic,200,coach,200,True,False,False +286,390,password_change_done,TRUE,200,basic,200,basic,200,coach,200,True,False,False +287,391,i18n_javascript,TRUE,200,basic,200,basic,200,coach,200,True,False,False +288,392,shortcut,TRUE,200,basic,200,basic,200,coach,200,True,False,False +289,393,changelist_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +290,394,add_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +291,395,autocomplete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +292,396,history_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +293,397,delete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +294,398,change_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +295,399,RedirectView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +296,568,app_index,TRUE,200,basic,200,basic,200,coach,200,True,False,False +297,571,landingview,TRUE,200,basic,200,basic,200,coach,200,True,False,False +298,573,get_blog_posts,TRUE,200,basic,200,basic,200,coach,200,True,False,False +299,576,AuthorizationView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +300,577,TokenView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +301,578,WorkoutViewSet,TRUE,200,basic,200,basic,200,coach,200,True,False,False +302,582,RowerViewSet,TRUE,200,basic,200,basic,200,coach,200,True,False,False +303,586,FavoriteChartViewSet,TRUE,200,basic,200,basic,200,coach,200,True,False,False +304,590,APIRootView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +305,592,SwaggerSchemaView,TRUE,200,basic,200,basic,200,coach,200,True,False,False +306,595,strokedatajson,TRUE,200,basic,200,basic,200,coach,200,True,False,True +307,596,error500_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +308,603,failed_queue_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False +309,604,failed_queue_empty,TRUE,200,basic,200,basic,200,coach,200,True,False,False +310,605,failed_job_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True From 17f2b568ff5dbc412848310e03806d0a65a8f79d Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 19 Jan 2020 12:34:04 +0100 Subject: [PATCH 43/80] working on one row --- rowers/tests/test_permissions2.py | 205 ++++++---- viewnames.csv | 624 +++++++++++++++--------------- 2 files changed, 451 insertions(+), 378 deletions(-) diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index 06313695..2cb95f0e 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -6,83 +6,107 @@ class PermissionsViewTests(TestCase): self.c = Client() ## Users - Pro, Basic, Coach & Self Coach - self.ucoach = UserFactory(username='coachuser') - self.rcoach = Rower.objects.create(user=self.ucoach, + ucoach = UserFactory(username='coachuser') + rcoach = Rower.objects.create(user=ucoach, birthdate=faker.profile()['birthdate'], gdproptin=True,surveydone=True,gdproptindate=timezone.now(), rowerplan='coach',clubsize=10) - self.ucoach_workouts = WorkoutFactory.create_batch(5, user=self.rcoach) - self.coachinggroup = CoachingGroup.objects.create() - self.rcoach.mycoachgroup = self.coachinggroup - self.rcoach.save() - self.factory = RequestFactory() - self.ucoachpassword = faker.word() - self.ucoach.set_password(self.ucoachpassword) - self.ucoach.save() + ucoach_workouts = WorkoutFactory.create_batch(5, user=rcoach) + coachinggroup = CoachingGroup.objects.create() + rcoach.mycoachgroup = coachinggroup + rcoach.save() - self.uplan = UserFactory(username='planuser') - self.rplan = Rower.objects.create(user=self.uplan, + ucoachpassword = faker.word() + ucoach.set_password(ucoachpassword) + ucoach.save() + + self.ucoach = { + 'user': ucoach, + 'password': ucoachpassword, + 'username': ucoach.username, + 'workouts': ucoach_workouts, + } + + uplan = UserFactory(username='planuser') + rplan = Rower.objects.create(user=uplan, birthdate=faker.profile()['birthdate'], gdproptin=True,surveydone=True,gdproptindate=timezone.now(), - rowerplan='plan') + rowerplan='plan',clubsize=10) - self.uplan_workouts = WorkoutFactory.create_batch(5, user=self.rplan) - self.factory = RequestFactory() - self.uplanpassword = faker.word() - self.uplan.set_password(self.uplanpassword) - self.uplan.save() + uplan_workouts = WorkoutFactory.create_batch(5, user=rplan) + rcoach.save() - self.upro = UserFactory(username='prouser') - self.rpro = Rower.objects.create(user=self.upro, + uplanpassword = faker.word() + uplan.set_password(uplanpassword) + uplan.save() + + self.uplan = { + 'user': uplan, + 'password': uplanpassword, + 'username': uplan.username, + 'workouts': uplan_workouts, + } + + uplan2 = UserFactory(username='plan2user') + rplan2 = Rower.objects.create(user=uplan2, birthdate=faker.profile()['birthdate'], gdproptin=True,surveydone=True,gdproptindate=timezone.now(), - rowerplan='pro') + rowerplan='plan',clubsize=10) - self.upro_workouts = WorkoutFactory.create_batch(5, user=self.rpro) - self.factory = RequestFactory() - self.upropassword = faker.word() - self.upro.set_password(self.upropassword) - self.upro.save() + uplan2_workouts = WorkoutFactory.create_batch(5, user=rplan2) + rcoach.save() - self.uplan2 = UserFactory(username='planuser2') - self.rplan2 = Rower.objects.create(user=self.uplan2, + uplan2password = faker.word() + uplan2.set_password(uplan2password) + uplan2.save() + + self.uplan2 = { + 'user': uplan2, + 'password': uplan2password, + 'username': uplan2.username, + 'workouts': uplan2_workouts, + } + + upro = UserFactory(username='prouser') + rpro = Rower.objects.create(user=upro, birthdate=faker.profile()['birthdate'], gdproptin=True,surveydone=True,gdproptindate=timezone.now(), - rowerplan='plan') + rowerplan='pro',clubsize=10) - self.uplan2_workouts = WorkoutFactory.create_batch(5, user=self.rplan2) - self.factory = RequestFactory() - self.uplan2password = faker.word() - self.uplan2.set_password(self.uplan2password) - self.uplan2.save() + upro_workouts = WorkoutFactory.create_batch(5, user=rpro) + rcoach.save() - self.upro2 = UserFactory(username='prouser2') - self.rpro2 = Rower.objects.create(user=self.upro2, + upropassword = faker.word() + upro.set_password(upropassword) + upro.save() + + self.upro = { + 'user': upro, + 'password': upropassword, + 'username': upro.username, + 'workouts': upro_workouts, + } + + ubasic = UserFactory(username='basicuser') + rbasic = Rower.objects.create(user=ubasic, birthdate=faker.profile()['birthdate'], gdproptin=True,surveydone=True,gdproptindate=timezone.now(), - rowerplan='pro') + rowerplan='basic',clubsize=10) - self.upro2_workouts = WorkoutFactory.create_batch(5, user=self.rpro2) - self.factory = RequestFactory() - self.upro2password = faker.word() - self.upro2.set_password(self.upro2password) - self.upro2.save() + ubasic_workouts = WorkoutFactory.create_batch(5, user=rbasic) + rcoach.save() - self.ubasic = UserFactory(username='basicuser') - self.rbasic = Rower.objects.create(user=self.ubasic, - birthdate=faker.profile()['birthdate'], - gdproptin=True,surveydone=True,gdproptindate=timezone.now(), - rowerplan='basic') - - self.ubasic_workouts = WorkoutFactory.create_batch(5, user=self.rbasic) - self.ubasic_workouts[0].privacy == 'private' - - self.factory = RequestFactory() - self.ubasicpassword = faker.word() - self.ubasic.set_password(self.ubasicpassword) - self.ubasic.save() + ubasicpassword = faker.word() + ubasic.set_password(ubasicpassword) + ubasic.save() + self.ubasic = { + 'user': ubasic, + 'password': ubasicpassword, + 'username': ubasic.username, + 'workouts': ubasic_workouts, + } ## TeamPro, TeamCoach, TeamSelfCoach @@ -90,17 +114,17 @@ class PermissionsViewTests(TestCase): self.teampro = Team.objects.create( name=faker.word(), notes=faker.text(), - manager=self.upro2) + manager=self.upro['user']) self.teamplan = Team.objects.create( name=faker.word()+'2', notes=faker.text(), - manager=self.uplan2) + manager=self.uplan2['user']) self.teamcoach = Team.objects.create( name=faker.word()+'3', notes=faker.text(), - manager=self.ucoach) + manager=self.ucoach['user']) viewstotest = [ ('workout_view', @@ -109,17 +133,23 @@ class PermissionsViewTests(TestCase): 'anonymous_response':200, 'own': 'basic', 'own_response': 200, + 'own_nonperm': 302, 'member': 'basic', 'member_response': 200, + 'member_nonperm': 404, 'coachee': 'coach', 'coachee_response': 200, - 'workoutid': False, - 'userid': True, + 'coachee_nonperm': 404, + 'is_staff': False, + 'workoutid': True, + 'userid': False, } ) ] + + @parameterized.expand(viewstotest) @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.read_df_sql') @@ -137,10 +167,53 @@ class PermissionsViewTests(TestCase): mocked_get_video_data, ): - for p in permissions: - url = reverse(view,kwargs=p['kwargs']) - if p['login']: - login = self.c.login(kwargs=p['login']) + if permissions['anonymous'] and not permissions['is_staff']: + urlstotest = [] + if permissions['workoutid']: + url = reverse(view,kwargs={'id':encoder.encode_hex(self.ucoach['workouts'][0].id)}) + urlstotest.append(url) + else: + url = reverse(view) - response = self.c.get(url,follow=True) - self.assertEqual(response.status_code,200) + for url in urlstotest: + print(url) + result = self.c.get(url) + self.assertEqual(result.status_code,permissions['anonymous_response']) + + if permissions['own'] and not permissions['is_staff']: + urlstotest = [] + falseurlstotest = [] + if permissions['own'] == 'basic': + thisuser = self.ubasic + notuser = None + elif permissions['own'] == 'pro': + thisuser = self.upro + notuser = self.ubasic + elif permissions['own'] == 'plan': + thisuser = self.uplan + notuser = self.upro + elif permissions['own'] == 'coach': + thisuser = self.ucoach + notuser = self.uplan + + + if permissions['workoutid']: + workouts = thisuser['workouts'] + url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) + urlstotest.append(url) + if notuser: + workouts = notuser['workouts'] + url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) + falseurlstotest.append(url) + + for url in urlstotest: + print(url) + login = self.c.login(username = thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['own_response']) + + for url in falseurlstotest: + print(url) + login = self.c.login(username = notuser['username'],password = notuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['own_response']) diff --git a/viewnames.csv b/viewnames.csv index e2047205..93b0bdc9 100644 --- a/viewnames.csv +++ b/viewnames.csv @@ -1,312 +1,312 @@ -,id,view,anonymous,anonymous_response,own,own_response,member,member_response,coachee,coachee_response,staff_only,userid,workoutid -0,0,workouts_summaries_email_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -1,1,rower_update_empower_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -2,2,agegroupcpview,TRUE,200,basic,200,basic,200,coach,200,True,False,False -3,4,ajax_agegrouprecords,TRUE,200,basic,200,basic,200,coach,200,True,False,False -4,5,fitness_metric_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -5,6,agegrouprecordview,TRUE,200,basic,200,basic,200,coach,200,True,False,False -6,7,workouts_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -7,8,virtualevents_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -8,9,virtualevent_create_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -9,10,indoorvirtualevent_create_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -10,11,virtualevent_toggle_email_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -11,12,indoorvirtualevent_toggle_email_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -12,13,virtualevent_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -13,14,virtualevent_ranking_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -14,15,virtualevent_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -15,16,indoorvirtualevent_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -16,17,virtualevent_register_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -17,18,indoorvirtualevent_register_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -18,19,virtualevent_addboat_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -19,20,virtualevent_withdraw_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -20,21,virtualevent_submit_result_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -21,22,virtualevent_disqualify_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -22,24,courses_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -23,25,course_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -24,26,addmanual_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -25,27,team_comparison_select,TRUE,200,basic,200,basic,200,coach,200,True,True,True -26,28,workouts_join_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -27,29,workouts_join_select,TRUE,200,basic,200,basic,200,coach,200,True,True,False -28,30,user_boxplot_select,TRUE,200,basic,200,basic,200,coach,200,True,True,False -29,31,analysis_new,TRUE,200,basic,200,basic,200,coach,200,True,True,False -30,32,user_multiflex_select,TRUE,200,basic,200,basic,200,coach,200,True,True,False -31,33,session_jobs_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -32,34,session_jobs_status,TRUE,200,basic,200,basic,200,coach,200,True,False,False -33,35,kill_async_job,TRUE,200,basic,200,basic,200,coach,200,True,False,False -34,36,post_progress,TRUE,200,basic,200,basic,200,coach,200,True,False,False -35,37,graphs_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -36,38,fitnessmetric_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -37,39,rankings_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -38,40,rankings_view2,TRUE,200,basic,200,basic,200,coach,200,True,False,False -39,41,otwrankings_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -40,42,oterankings_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -41,43,cum_flex,TRUE,200,basic,200,basic,200,coach,200,True,False,False -42,44,analysis_view_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False -43,47,cum_flex_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False -44,48,histo,TRUE,200,basic,200,basic,200,coach,200,True,False,False -45,49,histo_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False -46,51,cumstats,TRUE,200,basic,200,basic,200,coach,200,True,True,False -47,53,graph_show_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -48,54,GraphDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False -49,55,get_thumbnails,TRUE,200,basic,200,basic,200,coach,200,True,False,True -50,56,otw_use_impeller,TRUE,200,basic,200,basic,200,coach,200,True,False,True -51,57,otw_use_gps,TRUE,200,basic,200,basic,200,coach,200,True,False,True -52,58,workout_toggle_ranking,TRUE,200,basic,200,basic,200,coach,200,True,False,True -53,59,team_workout_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -54,60,workout_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -55,61,workout_histo_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -56,62,workout_forcecurve_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -57,63,workout_unsubscribe_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -58,64,workout_comment_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -59,65,workout_tcxemail_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -60,66,workout_gpxemail_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -61,67,workout_csvemail_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -62,68,workout_csvtoadmin_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -63,69,workout_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -64,70,workout_map_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -65,71,workout_update_cp_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -66,72,instroke_chart,TRUE,200,basic,200,basic,200,coach,200,True,False,False -67,73,instroke_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -68,74,workout_stats_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -69,75,workout_data_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -70,76,workout_otwsetpower_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -71,77,workout_otwpowerplot_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -72,78,workout_wind_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -73,79,workout_uploadimage_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -74,80,virtualevent_compare_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -75,81,virtualevent_uploadimage_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -76,82,virtualevent_setlogo_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -77,83,logo_delete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -78,84,workout_downloadwind_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -79,85,workout_downloadmetar_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -80,86,workout_stream_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -81,87,workout_summary_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -82,88,workout_summary_restore_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -83,89,workout_split_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -84,90,workout_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -85,91,workout_video_create_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -86,92,VideoDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False -87,93,workout_video_view_mini,TRUE,200,basic,200,basic,200,coach,200,True,False,True -88,94,workout_video_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -89,95,list_videos,TRUE,200,basic,200,basic,200,coach,200,True,False,False -90,96,video_selectworkout,TRUE,200,basic,200,basic,200,coach,200,True,False,False -91,98,workout_fusion_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -92,99,workout_fusion_list,TRUE,200,basic,200,basic,200,coach,200,True,False,True -93,100,TemplateView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -94,103,workout_add_chart_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -95,112,WorkoutDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False -96,113,workout_smoothenpace_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -97,114,workout_undo_smoothenpace_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -98,115,workout_c2import_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -99,120,workout_stravaimport_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -100,122,workout_getc2workout_all,TRUE,200,basic,200,basic,200,coach,200,True,False,False -101,124,workout_getimportview,TRUE,200,basic,200,basic,200,coach,200,True,False,False -102,125,workout_getstravaworkout_all,TRUE,200,basic,200,basic,200,coach,200,True,False,False -103,126,workout_getstravaworkout_next,TRUE,200,basic,200,basic,200,coach,200,True,False,False -104,127,workout_sporttracksimport_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -105,129,workout_getsporttracksworkout_all,TRUE,200,basic,200,basic,200,coach,200,True,False,False -106,130,workout_polarimport_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -107,132,workout_runkeeperimport_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -108,134,workout_underarmourimport_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -109,135,workout_c2_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -110,136,workout_strava_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -111,137,workout_recalcsummary_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -112,138,workout_sporttracks_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -113,139,workout_runkeeper_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -114,140,workout_underarmour_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -115,141,workout_tp_upload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -116,142,multi_compare_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -117,145,alerts_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -118,147,AlertDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False -119,148,alert_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -120,150,alert_create_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -121,152,alert_report_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -122,155,boxplot_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -123,157,boxplot_view_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False -124,158,multiflex_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -125,160,multiflex_data,TRUE,200,basic,200,basic,200,coach,200,True,False,False -126,161,deactivate_user,TRUE,200,basic,200,basic,200,coach,200,True,False,False -127,162,remove_user,TRUE,200,basic,200,basic,200,coach,200,True,False,False -128,163,user_gdpr_confirm,TRUE,200,basic,200,basic,200,coach,200,True,False,False -129,165,user_gdpr_optin,TRUE,200,basic,200,basic,200,coach,200,True,False,False -130,167,rower_teams_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -131,168,rower_calcdps_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -132,169,rower_exportsettings_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -133,171,team_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -134,172,team_members_stats_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -135,173,team_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -136,174,team_leaveconfirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -137,175,team_leave_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -138,176,team_deleteconfirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -139,177,team_requestmembership_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -140,178,reject_revoke_coach_request,TRUE,200,basic,200,basic,200,coach,200,True,False,False -141,179,coach_drop_athlete_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -142,180,coach_drop_athlete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -143,181,athlete_drop_coach_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -144,182,athlete_drop_coach_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -145,184,reject_revoke_coach_offer,TRUE,200,basic,200,basic,200,coach,200,True,False,False -146,186,request_coaching_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -147,187,offer_coaching_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -148,188,coach_accept_coachrequest_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -149,189,rower_accept_coachoffer_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -150,190,team_delete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -151,191,team_create_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -152,192,manager_member_drop_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -153,193,invitation_reject_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -154,194,invitation_revoke_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -155,195,rower_invitations_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -156,196,raise_500,TRUE,200,basic,200,basic,200,coach,200,True,False,False -157,198,request_revoke_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -158,199,request_reject_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -159,200,manager_requests_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -160,202,rower_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -161,204,rower_prefs_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -162,205,transactions_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -163,208,rower_c2_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False -164,209,rower_polar_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False -165,210,rower_revokeapp_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -166,211,rower_strava_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False -167,212,rower_sporttracks_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False -168,213,rower_underarmour_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False -169,214,rower_tp_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False -170,215,rower_runkeeper_authorize,TRUE,200,basic,200,basic,200,coach,200,True,False,False -171,216,rower_sporttracks_token_refresh,TRUE,200,basic,200,basic,200,coach,200,True,False,False -172,217,rower_underarmour_token_refresh,TRUE,200,basic,200,basic,200,coach,200,True,False,False -173,218,rower_tp_token_refresh,TRUE,200,basic,200,basic,200,coach,200,True,False,False -174,219,rower_c2_token_refresh,TRUE,200,basic,200,basic,200,coach,200,True,False,False -175,220,rower_favoritecharts_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -176,222,workout_workflow_config2_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -177,224,workflow_default_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -178,225,sendmail,TRUE,200,basic,200,basic,200,coach,200,True,False,False -179,231,analysis_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -180,232,laboratory_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -181,233,errormessage_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -182,237,payment_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -183,238,upgrade_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -184,240,downgrade_confirm_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -185,241,billing_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -186,242,upgrade_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -187,243,downgrade_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -188,244,payment_completed_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -189,245,downgrade_completed_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -190,246,paidplans_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -191,247,plan_stop_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -192,248,plan_tobasic_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -193,249,checkouts_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -194,250,upgrade_checkouts_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -195,251,downgrade_checkouts_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -196,252,planrequired_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -197,253,start_trial_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -198,254,start_plantrial_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -199,256,rower_register_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -200,257,freecoach_register_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -201,259,workout_workflow_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -202,260,workout_flexchart3_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True -203,264,rower_process_testcallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False -204,265,rower_create_trainingplan,TRUE,200,basic,200,basic,200,coach,200,True,False,False -205,267,TrainingPlanDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False -206,268,MicroCycleDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False -207,269,MesoCycleDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False -208,270,MacroCycleDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False -209,271,rower_trainingplan_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -210,279,rower_trainingplan_execution_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -211,281,TrainingMacroCycleUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False -212,282,TrainingMesoCycleUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False -213,283,planmacrocyclebymonth,TRUE,200,basic,200,basic,200,coach,200,True,False,False -214,285,planmesocyclebyweek,TRUE,200,basic,200,basic,200,coach,200,True,False,False -215,287,TrainingMicroCycleUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False -216,288,rower_delete_trainingtarget,TRUE,200,basic,200,basic,200,coach,200,True,False,False -217,289,TrainingPlanUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False -218,290,TrainingTargetUpdate,TRUE,200,basic,200,basic,200,coach,200,True,False,False -219,291,strokedataform,TRUE,200,basic,200,basic,200,coach,200,True,False,False -220,292,plannedsession_teamcreate_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -221,296,plannedsession_teamedit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -222,298,plannedsession_create_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -223,300,plannedsession_multiclone_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -224,302,plannedsession_multicreate_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -225,305,plannedsession_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -226,306,plannedsession_totemplate_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -227,307,plannedsession_compare_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -228,310,plannedsession_clone_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -229,311,plannedsession_teamclone_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -230,313,plannedsession_detach_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -231,315,plannedsession_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -232,317,PlannedSessionDelete,TRUE,200,basic,200,basic,200,coach,200,True,False,False -233,319,plannedsessions_manage_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -234,323,plannedsessions_coach_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -235,325,plannedsessions_coach_icsemail_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -236,326,plannedsessions_print_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -237,327,plannedsession_comment_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -238,330,plannedsessions_icsemail_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -239,332,plannedsessions_view,TRUE,200,basic,200,basic,200,coach,200,True,True,False -240,334,course_edit_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -241,335,course_delete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -242,336,course_kmldownload_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -243,337,course_replace_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -244,338,course_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -245,339,course_map_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -246,341,c2listdebug_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -247,343,rower_process_callback,TRUE,200,basic,200,basic,200,coach,200,True,False,False -248,344,rower_process_stravacallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False -249,345,rower_process_sporttrackscallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False -250,346,rower_process_underarmourcallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False -251,347,rower_process_polarcallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False -252,348,rower_process_runkeepercallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False -253,349,rower_process_tpcallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False -254,350,rower_process_twittercallback,TRUE,200,basic,200,basic,200,coach,200,True,False,False -255,351,set_language,TRUE,200,basic,200,basic,200,coach,200,True,False,False -256,352,SetOffsetView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -257,353,stats,TRUE,200,basic,200,basic,200,coach,200,True,False,False -258,354,stats_json,TRUE,200,basic,200,basic,200,coach,200,True,False,False -259,355,jobs,TRUE,200,basic,200,basic,200,coach,200,True,False,False -260,356,workers,TRUE,200,basic,200,basic,200,coach,200,True,False,False -261,357,worker_details,TRUE,200,basic,200,basic,200,coach,200,True,False,False -262,358,finished_jobs,TRUE,200,basic,200,basic,200,coach,200,True,False,False -263,359,started_jobs,TRUE,200,basic,200,basic,200,coach,200,True,False,False -264,360,deferred_jobs,TRUE,200,basic,200,basic,200,coach,200,True,False,False -265,361,clear_queue,TRUE,200,basic,200,basic,200,coach,200,True,False,False -266,362,requeue_all,TRUE,200,basic,200,basic,200,coach,200,True,False,False -267,363,job_detail,TRUE,200,basic,200,basic,200,coach,200,True,False,False -268,364,delete_job,TRUE,200,basic,200,basic,200,coach,200,True,False,False -269,365,actions,TRUE,200,basic,200,basic,200,coach,200,True,False,False -270,366,requeue_job_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -271,367,enqueue_job,TRUE,200,basic,200,basic,200,coach,200,True,False,False -272,368,JavaScriptCatalog,TRUE,200,basic,200,basic,200,coach,200,True,False,False -273,369,serve,TRUE,200,basic,200,basic,200,coach,200,True,False,False -274,371,LoginView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -275,372,LogoutView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -276,373,PasswordChangeView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -277,374,PasswordChangeDoneView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -278,375,PasswordResetView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -279,376,PasswordResetDoneView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -280,377,PasswordResetConfirmView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -281,378,PasswordResetCompleteView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -282,386,index,TRUE,200,basic,200,basic,200,coach,200,True,False,False -283,387,login,TRUE,200,basic,200,basic,200,coach,200,True,False,False -284,388,logout,TRUE,200,basic,200,basic,200,coach,200,True,False,False -285,389,password_change,TRUE,200,basic,200,basic,200,coach,200,True,False,False -286,390,password_change_done,TRUE,200,basic,200,basic,200,coach,200,True,False,False -287,391,i18n_javascript,TRUE,200,basic,200,basic,200,coach,200,True,False,False -288,392,shortcut,TRUE,200,basic,200,basic,200,coach,200,True,False,False -289,393,changelist_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -290,394,add_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -291,395,autocomplete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -292,396,history_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -293,397,delete_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -294,398,change_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -295,399,RedirectView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -296,568,app_index,TRUE,200,basic,200,basic,200,coach,200,True,False,False -297,571,landingview,TRUE,200,basic,200,basic,200,coach,200,True,False,False -298,573,get_blog_posts,TRUE,200,basic,200,basic,200,coach,200,True,False,False -299,576,AuthorizationView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -300,577,TokenView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -301,578,WorkoutViewSet,TRUE,200,basic,200,basic,200,coach,200,True,False,False -302,582,RowerViewSet,TRUE,200,basic,200,basic,200,coach,200,True,False,False -303,586,FavoriteChartViewSet,TRUE,200,basic,200,basic,200,coach,200,True,False,False -304,590,APIRootView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -305,592,SwaggerSchemaView,TRUE,200,basic,200,basic,200,coach,200,True,False,False -306,595,strokedatajson,TRUE,200,basic,200,basic,200,coach,200,True,False,True -307,596,error500_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -308,603,failed_queue_view,TRUE,200,basic,200,basic,200,coach,200,True,False,False -309,604,failed_queue_empty,TRUE,200,basic,200,basic,200,coach,200,True,False,False -310,605,failed_job_view,TRUE,200,basic,200,basic,200,coach,200,True,False,True +,id,view,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid +0,0,workouts_summaries_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +1,1,rower_update_empower_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +2,2,agegroupcpview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +3,4,ajax_agegrouprecords,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +4,5,fitness_metric_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +5,6,agegrouprecordview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +6,7,workouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +7,8,virtualevents_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +8,9,virtualevent_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +9,10,indoorvirtualevent_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +10,11,virtualevent_toggle_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +11,12,indoorvirtualevent_toggle_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +12,13,virtualevent_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +13,14,virtualevent_ranking_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +14,15,virtualevent_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +15,16,indoorvirtualevent_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +16,17,virtualevent_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +17,18,indoorvirtualevent_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +18,19,virtualevent_addboat_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +19,20,virtualevent_withdraw_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +20,21,virtualevent_submit_result_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +21,22,virtualevent_disqualify_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +22,24,courses_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +23,25,course_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +24,26,addmanual_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +25,27,team_comparison_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,True +26,28,workouts_join_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +27,29,workouts_join_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +28,30,user_boxplot_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +29,31,analysis_new,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +30,32,user_multiflex_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +31,33,session_jobs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +32,34,session_jobs_status,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +33,35,kill_async_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +34,36,post_progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +35,37,graphs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +36,38,fitnessmetric_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +37,39,rankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +38,40,rankings_view2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +39,41,otwrankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +40,42,oterankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +41,43,cum_flex,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +42,44,analysis_view_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +43,47,cum_flex_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +44,48,histo,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +45,49,histo_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +46,51,cumstats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +47,53,graph_show_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +48,54,GraphDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +49,55,get_thumbnails,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +50,56,otw_use_impeller,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +51,57,otw_use_gps,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +52,58,workout_toggle_ranking,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +53,59,team_workout_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +54,60,workout_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +55,61,workout_histo_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +56,62,workout_forcecurve_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +57,63,workout_unsubscribe_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +58,64,workout_comment_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +59,65,workout_tcxemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +60,66,workout_gpxemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +61,67,workout_csvemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +62,68,workout_csvtoadmin_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +63,69,workout_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +64,70,workout_map_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +65,71,workout_update_cp_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +66,72,instroke_chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +67,73,instroke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +68,74,workout_stats_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +69,75,workout_data_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +70,76,workout_otwsetpower_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +71,77,workout_otwpowerplot_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +72,78,workout_wind_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +73,79,workout_uploadimage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +74,80,virtualevent_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +75,81,virtualevent_uploadimage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +76,82,virtualevent_setlogo_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +77,83,logo_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +78,84,workout_downloadwind_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +79,85,workout_downloadmetar_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +80,86,workout_stream_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +81,87,workout_summary_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +82,88,workout_summary_restore_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +83,89,workout_split_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +84,90,workout_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +85,91,workout_video_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +86,92,VideoDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +87,93,workout_video_view_mini,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +88,94,workout_video_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +89,95,list_videos,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +90,96,video_selectworkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +91,98,workout_fusion_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +92,99,workout_fusion_list,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +93,100,TemplateView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +94,103,workout_add_chart_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +95,112,WorkoutDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +96,113,workout_smoothenpace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +97,114,workout_undo_smoothenpace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +98,115,workout_c2import_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +99,120,workout_stravaimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +100,122,workout_getc2workout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +101,124,workout_getimportview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +102,125,workout_getstravaworkout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +103,126,workout_getstravaworkout_next,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +104,127,workout_sporttracksimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +105,129,workout_getsporttracksworkout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +106,130,workout_polarimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +107,132,workout_runkeeperimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +108,134,workout_underarmourimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +109,135,workout_c2_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +110,136,workout_strava_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +111,137,workout_recalcsummary_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +112,138,workout_sporttracks_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +113,139,workout_runkeeper_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +114,140,workout_underarmour_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +115,141,workout_tp_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +116,142,multi_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +117,145,alerts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +118,147,AlertDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +119,148,alert_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +120,150,alert_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +121,152,alert_report_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +122,155,boxplot_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +123,157,boxplot_view_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +124,158,multiflex_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +125,160,multiflex_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +126,161,deactivate_user,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +127,162,remove_user,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +128,163,user_gdpr_confirm,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +129,165,user_gdpr_optin,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +130,167,rower_teams_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +131,168,rower_calcdps_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +132,169,rower_exportsettings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +133,171,team_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +134,172,team_members_stats_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +135,173,team_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +136,174,team_leaveconfirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +137,175,team_leave_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +138,176,team_deleteconfirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +139,177,team_requestmembership_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +140,178,reject_revoke_coach_request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +141,179,coach_drop_athlete_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +142,180,coach_drop_athlete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +143,181,athlete_drop_coach_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +144,182,athlete_drop_coach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +145,184,reject_revoke_coach_offer,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +146,186,request_coaching_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +147,187,offer_coaching_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +148,188,coach_accept_coachrequest_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +149,189,rower_accept_coachoffer_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +150,190,team_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +151,191,team_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +152,192,manager_member_drop_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +153,193,invitation_reject_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +154,194,invitation_revoke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +155,195,rower_invitations_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +156,196,raise_500,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +157,198,request_revoke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +158,199,request_reject_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +159,200,manager_requests_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +160,202,rower_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +161,204,rower_prefs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +162,205,transactions_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +163,208,rower_c2_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +164,209,rower_polar_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +165,210,rower_revokeapp_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +166,211,rower_strava_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +167,212,rower_sporttracks_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +168,213,rower_underarmour_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +169,214,rower_tp_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +170,215,rower_runkeeper_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +171,216,rower_sporttracks_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +172,217,rower_underarmour_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +173,218,rower_tp_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +174,219,rower_c2_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +175,220,rower_favoritecharts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +176,222,workout_workflow_config2_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +177,224,workflow_default_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +178,225,sendmail,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +179,231,analysis_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +180,232,laboratory_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +181,233,errormessage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +182,237,payment_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +183,238,upgrade_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +184,240,downgrade_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +185,241,billing_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +186,242,upgrade_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +187,243,downgrade_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +188,244,payment_completed_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +189,245,downgrade_completed_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +190,246,paidplans_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +191,247,plan_stop_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +192,248,plan_tobasic_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +193,249,checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +194,250,upgrade_checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +195,251,downgrade_checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +196,252,planrequired_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +197,253,start_trial_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +198,254,start_plantrial_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +199,256,rower_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +200,257,freecoach_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +201,259,workout_workflow_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +202,260,workout_flexchart3_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +203,264,rower_process_testcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +204,265,rower_create_trainingplan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +205,267,TrainingPlanDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +206,268,MicroCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +207,269,MesoCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +208,270,MacroCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +209,271,rower_trainingplan_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +210,279,rower_trainingplan_execution_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +211,281,TrainingMacroCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +212,282,TrainingMesoCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +213,283,planmacrocyclebymonth,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +214,285,planmesocyclebyweek,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +215,287,TrainingMicroCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +216,288,rower_delete_trainingtarget,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +217,289,TrainingPlanUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +218,290,TrainingTargetUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +219,291,strokedataform,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +220,292,plannedsession_teamcreate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +221,296,plannedsession_teamedit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +222,298,plannedsession_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +223,300,plannedsession_multiclone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +224,302,plannedsession_multicreate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +225,305,plannedsession_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +226,306,plannedsession_totemplate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +227,307,plannedsession_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +228,310,plannedsession_clone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +229,311,plannedsession_teamclone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +230,313,plannedsession_detach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +231,315,plannedsession_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +232,317,PlannedSessionDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +233,319,plannedsessions_manage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +234,323,plannedsessions_coach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +235,325,plannedsessions_coach_icsemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +236,326,plannedsessions_print_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +237,327,plannedsession_comment_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +238,330,plannedsessions_icsemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +239,332,plannedsessions_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False +240,334,course_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +241,335,course_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +242,336,course_kmldownload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +243,337,course_replace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +244,338,course_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +245,339,course_map_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +246,341,c2listdebug_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +247,343,rower_process_callback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +248,344,rower_process_stravacallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +249,345,rower_process_sporttrackscallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +250,346,rower_process_underarmourcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +251,347,rower_process_polarcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +252,348,rower_process_runkeepercallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +253,349,rower_process_tpcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +254,350,rower_process_twittercallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +255,351,set_language,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +256,352,SetOffsetView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +257,353,stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +258,354,stats_json,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +259,355,jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +260,356,workers,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +261,357,worker_details,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +262,358,finished_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +263,359,started_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +264,360,deferred_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +265,361,clear_queue,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +266,362,requeue_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +267,363,job_detail,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +268,364,delete_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +269,365,actions,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +270,366,requeue_job_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +271,367,enqueue_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +272,368,JavaScriptCatalog,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +273,369,serve,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +274,371,LoginView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +275,372,LogoutView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +276,373,PasswordChangeView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +277,374,PasswordChangeDoneView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +278,375,PasswordResetView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +279,376,PasswordResetDoneView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +280,377,PasswordResetConfirmView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +281,378,PasswordResetCompleteView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +282,386,index,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +283,387,login,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +284,388,logout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +285,389,password_change,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +286,390,password_change_done,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +287,391,i18n_javascript,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +288,392,shortcut,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +289,393,changelist_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +290,394,add_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +291,395,autocomplete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +292,396,history_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +293,397,delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +294,398,change_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +295,399,RedirectView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +296,568,app_index,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +297,571,landingview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +298,573,get_blog_posts,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +299,576,AuthorizationView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +300,577,TokenView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +301,578,WorkoutViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +302,582,RowerViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +303,586,FavoriteChartViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +304,590,APIRootView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +305,592,SwaggerSchemaView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +306,595,strokedatajson,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +307,596,error500_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +308,603,failed_queue_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +309,604,failed_queue_empty,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +310,605,failed_job_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True From 69f95ae852e86888b17039d947868d3aa7361ceb Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 19 Jan 2020 12:43:41 +0100 Subject: [PATCH 44/80] need to fix check for list-workouts for strange user --- rowers/tests/test_permissions2.py | 50 ++- viewnames.csv | 620 +++++++++++++++--------------- 2 files changed, 357 insertions(+), 313 deletions(-) diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index 2cb95f0e..e9c6c14a 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -108,6 +108,26 @@ class PermissionsViewTests(TestCase): 'workouts': ubasic_workouts, } + ustrange = UserFactory(username='strangeuser') + rstrange = Rower.objects.create(user=ustrange, + birthdate=faker.profile()['birthdate'], + gdproptin=True,surveydone=True,gdproptindate=timezone.now(), + rowerplan='basic',clubsize=10) + + ustrange_workouts = WorkoutFactory.create_batch(5, user=rstrange) + rcoach.save() + + ustrangepassword = faker.word() + ustrange.set_password(ustrangepassword) + ustrange.save() + + self.ustrange = { + 'user': ustrange, + 'password': ustrangepassword, + 'username': ustrange.username, + 'workouts': ustrange_workouts, + } + ## TeamPro, TeamCoach, TeamSelfCoach @@ -144,6 +164,24 @@ class PermissionsViewTests(TestCase): 'workoutid': True, 'userid': False, } + ), + ('workouts_view', + { + 'anonymous': False, + 'anonymous_response':302, + 'own': 'basic', + 'own_response': 200, + 'own_nonperm': 302, + 'member': 'basic', + 'member_response': 200, + 'member_nonperm': 404, + 'coachee': 'coach', + 'coachee_response': 200, + 'coachee_nonperm': 404, + 'is_staff': False, + 'workoutid': False, + 'userid': True, + } ) ] @@ -205,15 +243,21 @@ class PermissionsViewTests(TestCase): workouts = notuser['workouts'] url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) falseurlstotest.append(url) + elif permissions['userid']: + url = reverse(view,kwargs={'userid':thisuser['user'].id}) + urlstotest.append(url) + if notuser: + url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) + falseurlstotest.append(url) - for url in urlstotest: + for url in urlstotest: print(url) login = self.c.login(username = thisuser['username'],password = thisuser['password']) result = self.c.get(url) self.assertEqual(result.status_code, permissions['own_response']) - for url in falseurlstotest: + for url in falseurlstotest: print(url) login = self.c.login(username = notuser['username'],password = notuser['password']) result = self.c.get(url) - self.assertEqual(result.status_code, permissions['own_response']) + self.assertEqual(result.status_code, permissions['own_nonperm']) diff --git a/viewnames.csv b/viewnames.csv index 93b0bdc9..9a62770a 100644 --- a/viewnames.csv +++ b/viewnames.csv @@ -1,312 +1,312 @@ ,id,view,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid 0,0,workouts_summaries_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -1,1,rower_update_empower_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -2,2,agegroupcpview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -3,4,ajax_agegrouprecords,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -4,5,fitness_metric_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -5,6,agegrouprecordview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -6,7,workouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -7,8,virtualevents_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -8,9,virtualevent_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -9,10,indoorvirtualevent_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -10,11,virtualevent_toggle_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -11,12,indoorvirtualevent_toggle_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -12,13,virtualevent_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -13,14,virtualevent_ranking_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -14,15,virtualevent_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -15,16,indoorvirtualevent_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -16,17,virtualevent_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -17,18,indoorvirtualevent_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -18,19,virtualevent_addboat_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -19,20,virtualevent_withdraw_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -20,21,virtualevent_submit_result_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -21,22,virtualevent_disqualify_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -22,24,courses_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -23,25,course_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -24,26,addmanual_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -25,27,team_comparison_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,True -26,28,workouts_join_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -27,29,workouts_join_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -28,30,user_boxplot_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -29,31,analysis_new,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -30,32,user_multiflex_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -31,33,session_jobs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -32,34,session_jobs_status,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -33,35,kill_async_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -34,36,post_progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -35,37,graphs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -36,38,fitnessmetric_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -37,39,rankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -38,40,rankings_view2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -39,41,otwrankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -40,42,oterankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -41,43,cum_flex,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -42,44,analysis_view_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -43,47,cum_flex_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -44,48,histo,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -45,49,histo_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -46,51,cumstats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -47,53,graph_show_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -48,54,GraphDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -49,55,get_thumbnails,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -50,56,otw_use_impeller,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -51,57,otw_use_gps,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -52,58,workout_toggle_ranking,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -53,59,team_workout_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -54,60,workout_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -55,61,workout_histo_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -56,62,workout_forcecurve_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -57,63,workout_unsubscribe_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -58,64,workout_comment_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -59,65,workout_tcxemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -60,66,workout_gpxemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -61,67,workout_csvemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -62,68,workout_csvtoadmin_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -63,69,workout_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -64,70,workout_map_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -65,71,workout_update_cp_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -66,72,instroke_chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -67,73,instroke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -68,74,workout_stats_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -69,75,workout_data_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -70,76,workout_otwsetpower_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -71,77,workout_otwpowerplot_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -72,78,workout_wind_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -73,79,workout_uploadimage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -74,80,virtualevent_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -75,81,virtualevent_uploadimage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -76,82,virtualevent_setlogo_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -77,83,logo_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -78,84,workout_downloadwind_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -79,85,workout_downloadmetar_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -80,86,workout_stream_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -81,87,workout_summary_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -82,88,workout_summary_restore_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -83,89,workout_split_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -84,90,workout_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -85,91,workout_video_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -86,92,VideoDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -87,93,workout_video_view_mini,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -88,94,workout_video_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -89,95,list_videos,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -90,96,video_selectworkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -91,98,workout_fusion_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -92,99,workout_fusion_list,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -93,100,TemplateView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -94,103,workout_add_chart_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -95,112,WorkoutDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -96,113,workout_smoothenpace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -97,114,workout_undo_smoothenpace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -98,115,workout_c2import_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -99,120,workout_stravaimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -100,122,workout_getc2workout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -101,124,workout_getimportview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -102,125,workout_getstravaworkout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -103,126,workout_getstravaworkout_next,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -104,127,workout_sporttracksimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -105,129,workout_getsporttracksworkout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -106,130,workout_polarimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -107,132,workout_runkeeperimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -108,134,workout_underarmourimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -109,135,workout_c2_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -110,136,workout_strava_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -111,137,workout_recalcsummary_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -112,138,workout_sporttracks_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -113,139,workout_runkeeper_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -114,140,workout_underarmour_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -115,141,workout_tp_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -116,142,multi_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -117,145,alerts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -118,147,AlertDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -119,148,alert_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -120,150,alert_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -121,152,alert_report_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -122,155,boxplot_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -123,157,boxplot_view_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -124,158,multiflex_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -125,160,multiflex_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -126,161,deactivate_user,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -127,162,remove_user,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -128,163,user_gdpr_confirm,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -129,165,user_gdpr_optin,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -130,167,rower_teams_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -131,168,rower_calcdps_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -132,169,rower_exportsettings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -133,171,team_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -134,172,team_members_stats_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -135,173,team_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -136,174,team_leaveconfirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -137,175,team_leave_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -138,176,team_deleteconfirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -139,177,team_requestmembership_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -140,178,reject_revoke_coach_request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -141,179,coach_drop_athlete_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -142,180,coach_drop_athlete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -143,181,athlete_drop_coach_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -144,182,athlete_drop_coach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -145,184,reject_revoke_coach_offer,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -146,186,request_coaching_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -147,187,offer_coaching_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -148,188,coach_accept_coachrequest_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -149,189,rower_accept_coachoffer_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -150,190,team_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -151,191,team_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -152,192,manager_member_drop_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -153,193,invitation_reject_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -154,194,invitation_revoke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -155,195,rower_invitations_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -156,196,raise_500,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -157,198,request_revoke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -158,199,request_reject_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -159,200,manager_requests_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -160,202,rower_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -161,204,rower_prefs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -162,205,transactions_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -163,208,rower_c2_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -164,209,rower_polar_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -165,210,rower_revokeapp_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -166,211,rower_strava_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -167,212,rower_sporttracks_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -168,213,rower_underarmour_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -169,214,rower_tp_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -170,215,rower_runkeeper_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -171,216,rower_sporttracks_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -172,217,rower_underarmour_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -173,218,rower_tp_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -174,219,rower_c2_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -175,220,rower_favoritecharts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -176,222,workout_workflow_config2_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -177,224,workflow_default_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -178,225,sendmail,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -179,231,analysis_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -180,232,laboratory_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -181,233,errormessage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -182,237,payment_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -183,238,upgrade_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -184,240,downgrade_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -185,241,billing_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -186,242,upgrade_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -187,243,downgrade_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -188,244,payment_completed_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -189,245,downgrade_completed_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -190,246,paidplans_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -191,247,plan_stop_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -192,248,plan_tobasic_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -193,249,checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -194,250,upgrade_checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -195,251,downgrade_checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -196,252,planrequired_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -197,253,start_trial_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -198,254,start_plantrial_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -199,256,rower_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -200,257,freecoach_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -201,259,workout_workflow_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -202,260,workout_flexchart3_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -203,264,rower_process_testcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -204,265,rower_create_trainingplan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -205,267,TrainingPlanDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -206,268,MicroCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -207,269,MesoCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -208,270,MacroCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -209,271,rower_trainingplan_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -210,279,rower_trainingplan_execution_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -211,281,TrainingMacroCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -212,282,TrainingMesoCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -213,283,planmacrocyclebymonth,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -214,285,planmesocyclebyweek,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -215,287,TrainingMicroCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -216,288,rower_delete_trainingtarget,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -217,289,TrainingPlanUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -218,290,TrainingTargetUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -219,291,strokedataform,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -220,292,plannedsession_teamcreate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -221,296,plannedsession_teamedit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -222,298,plannedsession_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -223,300,plannedsession_multiclone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -224,302,plannedsession_multicreate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -225,305,plannedsession_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -226,306,plannedsession_totemplate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -227,307,plannedsession_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -228,310,plannedsession_clone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -229,311,plannedsession_teamclone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -230,313,plannedsession_detach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -231,315,plannedsession_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -232,317,PlannedSessionDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -233,319,plannedsessions_manage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -234,323,plannedsessions_coach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -235,325,plannedsessions_coach_icsemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -236,326,plannedsessions_print_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -237,327,plannedsession_comment_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -238,330,plannedsessions_icsemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -239,332,plannedsessions_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,True,False -240,334,course_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -241,335,course_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -242,336,course_kmldownload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -243,337,course_replace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -244,338,course_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -245,339,course_map_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -246,341,c2listdebug_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -247,343,rower_process_callback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -248,344,rower_process_stravacallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -249,345,rower_process_sporttrackscallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -250,346,rower_process_underarmourcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -251,347,rower_process_polarcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -252,348,rower_process_runkeepercallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -253,349,rower_process_tpcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -254,350,rower_process_twittercallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -255,351,set_language,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -256,352,SetOffsetView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -257,353,stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -258,354,stats_json,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -259,355,jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -260,356,workers,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -261,357,worker_details,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -262,358,finished_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -263,359,started_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -264,360,deferred_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -265,361,clear_queue,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -266,362,requeue_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -267,363,job_detail,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -268,364,delete_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -269,365,actions,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -270,366,requeue_job_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -271,367,enqueue_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -272,368,JavaScriptCatalog,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -273,369,serve,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -274,371,LoginView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -275,372,LogoutView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -276,373,PasswordChangeView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -277,374,PasswordChangeDoneView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -278,375,PasswordResetView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -279,376,PasswordResetDoneView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -280,377,PasswordResetConfirmView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -281,378,PasswordResetCompleteView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -282,386,index,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -283,387,login,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -284,388,logout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -285,389,password_change,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -286,390,password_change_done,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -287,391,i18n_javascript,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -288,392,shortcut,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -289,393,changelist_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -290,394,add_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -291,395,autocomplete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -292,396,history_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -293,397,delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -294,398,change_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -295,399,RedirectView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -296,568,app_index,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -297,571,landingview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -298,573,get_blog_posts,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -299,576,AuthorizationView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -300,577,TokenView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -301,578,WorkoutViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -302,582,RowerViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -303,586,FavoriteChartViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -304,590,APIRootView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -305,592,SwaggerSchemaView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -306,595,strokedatajson,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True -307,596,error500_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -308,603,failed_queue_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -309,604,failed_queue_empty,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -310,605,failed_job_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,True +1,1,rower_update_empower_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +2,2,agegroupcpview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +3,4,ajax_agegrouprecords,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +4,5,fitness_metric_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +5,6,agegrouprecordview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +6,7,workouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +7,8,virtualevents_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +8,9,virtualevent_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +9,10,indoorvirtualevent_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +10,11,virtualevent_toggle_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +11,12,indoorvirtualevent_toggle_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +12,13,virtualevent_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +13,14,virtualevent_ranking_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +14,15,virtualevent_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +15,16,indoorvirtualevent_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +16,17,virtualevent_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +17,18,indoorvirtualevent_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +18,19,virtualevent_addboat_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +19,20,virtualevent_withdraw_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +20,21,virtualevent_submit_result_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +21,22,virtualevent_disqualify_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +22,24,courses_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +23,25,course_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +24,26,addmanual_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +25,27,team_comparison_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,True +26,28,workouts_join_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +27,29,workouts_join_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +28,30,user_boxplot_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +29,31,analysis_new,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +30,32,user_multiflex_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +31,33,session_jobs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +32,34,session_jobs_status,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +33,35,kill_async_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +34,36,post_progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +35,37,graphs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +36,38,fitnessmetric_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +37,39,rankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +38,40,rankings_view2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +39,41,otwrankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +40,42,oterankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +41,43,cum_flex,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +42,44,analysis_view_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +43,47,cum_flex_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +44,48,histo,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +45,49,histo_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +46,51,cumstats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +47,53,graph_show_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +48,54,GraphDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +49,55,get_thumbnails,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +50,56,otw_use_impeller,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +51,57,otw_use_gps,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +52,58,workout_toggle_ranking,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +53,59,team_workout_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +54,60,workout_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +55,61,workout_histo_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +56,62,workout_forcecurve_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +57,63,workout_unsubscribe_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +58,64,workout_comment_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +59,65,workout_tcxemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +60,66,workout_gpxemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +61,67,workout_csvemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +62,68,workout_csvtoadmin_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +63,69,workout_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +64,70,workout_map_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +65,71,workout_update_cp_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +66,72,instroke_chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +67,73,instroke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +68,74,workout_stats_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +69,75,workout_data_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +70,76,workout_otwsetpower_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +71,77,workout_otwpowerplot_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +72,78,workout_wind_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +73,79,workout_uploadimage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +74,80,virtualevent_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +75,81,virtualevent_uploadimage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +76,82,virtualevent_setlogo_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +77,83,logo_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +78,84,workout_downloadwind_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +79,85,workout_downloadmetar_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +80,86,workout_stream_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +81,87,workout_summary_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +82,88,workout_summary_restore_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +83,89,workout_split_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +84,90,workout_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +85,91,workout_video_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +86,92,VideoDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +87,93,workout_video_view_mini,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +88,94,workout_video_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +89,95,list_videos,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +90,96,video_selectworkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +91,98,workout_fusion_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +92,99,workout_fusion_list,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +93,100,TemplateView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +94,103,workout_add_chart_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +95,112,WorkoutDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +96,113,workout_smoothenpace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +97,114,workout_undo_smoothenpace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +98,115,workout_c2import_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +99,120,workout_stravaimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +100,122,workout_getc2workout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +101,124,workout_getimportview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +102,125,workout_getstravaworkout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +103,126,workout_getstravaworkout_next,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +104,127,workout_sporttracksimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +105,129,workout_getsporttracksworkout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +106,130,workout_polarimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +107,132,workout_runkeeperimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +108,134,workout_underarmourimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +109,135,workout_c2_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +110,136,workout_strava_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +111,137,workout_recalcsummary_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +112,138,workout_sporttracks_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +113,139,workout_runkeeper_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +114,140,workout_underarmour_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +115,141,workout_tp_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +116,142,multi_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +117,145,alerts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +118,147,AlertDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +119,148,alert_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +120,150,alert_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +121,152,alert_report_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +122,155,boxplot_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +123,157,boxplot_view_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +124,158,multiflex_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +125,160,multiflex_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +126,161,deactivate_user,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +127,162,remove_user,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +128,163,user_gdpr_confirm,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +129,165,user_gdpr_optin,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +130,167,rower_teams_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +131,168,rower_calcdps_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +132,169,rower_exportsettings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +133,171,team_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +134,172,team_members_stats_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +135,173,team_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +136,174,team_leaveconfirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +137,175,team_leave_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +138,176,team_deleteconfirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +139,177,team_requestmembership_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +140,178,reject_revoke_coach_request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +141,179,coach_drop_athlete_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +142,180,coach_drop_athlete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +143,181,athlete_drop_coach_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +144,182,athlete_drop_coach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +145,184,reject_revoke_coach_offer,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +146,186,request_coaching_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +147,187,offer_coaching_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +148,188,coach_accept_coachrequest_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +149,189,rower_accept_coachoffer_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +150,190,team_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +151,191,team_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +152,192,manager_member_drop_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +153,193,invitation_reject_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +154,194,invitation_revoke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +155,195,rower_invitations_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +156,196,raise_500,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +157,198,request_revoke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +158,199,request_reject_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +159,200,manager_requests_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +160,202,rower_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +161,204,rower_prefs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +162,205,transactions_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +163,208,rower_c2_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +164,209,rower_polar_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +165,210,rower_revokeapp_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +166,211,rower_strava_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +167,212,rower_sporttracks_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +168,213,rower_underarmour_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +169,214,rower_tp_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +170,215,rower_runkeeper_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +171,216,rower_sporttracks_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +172,217,rower_underarmour_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +173,218,rower_tp_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +174,219,rower_c2_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +175,220,rower_favoritecharts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +176,222,workout_workflow_config2_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +177,224,workflow_default_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +178,225,sendmail,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +179,231,analysis_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +180,232,laboratory_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +181,233,errormessage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +182,237,payment_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +183,238,upgrade_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +184,240,downgrade_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +185,241,billing_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +186,242,upgrade_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +187,243,downgrade_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +188,244,payment_completed_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +189,245,downgrade_completed_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +190,246,paidplans_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +191,247,plan_stop_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +192,248,plan_tobasic_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +193,249,checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +194,250,upgrade_checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +195,251,downgrade_checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +196,252,planrequired_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +197,253,start_trial_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +198,254,start_plantrial_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +199,256,rower_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +200,257,freecoach_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +201,259,workout_workflow_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +202,260,workout_flexchart3_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +203,264,rower_process_testcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +204,265,rower_create_trainingplan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +205,267,TrainingPlanDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +206,268,MicroCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +207,269,MesoCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +208,270,MacroCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +209,271,rower_trainingplan_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +210,279,rower_trainingplan_execution_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +211,281,TrainingMacroCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +212,282,TrainingMesoCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +213,283,planmacrocyclebymonth,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +214,285,planmesocyclebyweek,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +215,287,TrainingMicroCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +216,288,rower_delete_trainingtarget,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +217,289,TrainingPlanUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +218,290,TrainingTargetUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +219,291,strokedataform,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +220,292,plannedsession_teamcreate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +221,296,plannedsession_teamedit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +222,298,plannedsession_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +223,300,plannedsession_multiclone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +224,302,plannedsession_multicreate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +225,305,plannedsession_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +226,306,plannedsession_totemplate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +227,307,plannedsession_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +228,310,plannedsession_clone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +229,311,plannedsession_teamclone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +230,313,plannedsession_detach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +231,315,plannedsession_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +232,317,PlannedSessionDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +233,319,plannedsessions_manage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +234,323,plannedsessions_coach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +235,325,plannedsessions_coach_icsemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +236,326,plannedsessions_print_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +237,327,plannedsession_comment_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +238,330,plannedsessions_icsemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +239,332,plannedsessions_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +240,334,course_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +241,335,course_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +242,336,course_kmldownload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +243,337,course_replace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +244,338,course_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +245,339,course_map_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +246,341,c2listdebug_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +247,343,rower_process_callback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +248,344,rower_process_stravacallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +249,345,rower_process_sporttrackscallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +250,346,rower_process_underarmourcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +251,347,rower_process_polarcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +252,348,rower_process_runkeepercallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +253,349,rower_process_tpcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +254,350,rower_process_twittercallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +255,351,set_language,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +256,352,SetOffsetView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +257,353,stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +258,354,stats_json,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +259,355,jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +260,356,workers,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +261,357,worker_details,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +262,358,finished_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +263,359,started_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +264,360,deferred_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +265,361,clear_queue,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +266,362,requeue_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +267,363,job_detail,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +268,364,delete_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +269,365,actions,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +270,366,requeue_job_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +271,367,enqueue_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +272,368,JavaScriptCatalog,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +273,369,serve,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +274,371,LoginView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +275,372,LogoutView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +276,373,PasswordChangeView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +277,374,PasswordChangeDoneView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +278,375,PasswordResetView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +279,376,PasswordResetDoneView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +280,377,PasswordResetConfirmView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +281,378,PasswordResetCompleteView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +282,386,index,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +283,387,login,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +284,388,logout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +285,389,password_change,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +286,390,password_change_done,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +287,391,i18n_javascript,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +288,392,shortcut,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +289,393,changelist_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +290,394,add_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +291,395,autocomplete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +292,396,history_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +293,397,delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +294,398,change_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +295,399,RedirectView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +296,568,app_index,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +297,571,landingview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +298,573,get_blog_posts,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +299,576,AuthorizationView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +300,577,TokenView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +301,578,WorkoutViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +302,582,RowerViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +303,586,FavoriteChartViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +304,590,APIRootView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +305,592,SwaggerSchemaView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +306,595,strokedatajson,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +307,596,error500_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +308,603,failed_queue_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +309,604,failed_queue_empty,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +310,605,failed_job_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True From b8bc152248ab1d7e3d77dfdc433402160a246282 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 20 Jan 2020 08:31:20 +0100 Subject: [PATCH 45/80] moving views.csv file to tests folder where it belongs --- viewnames.csv => rowers/tests/viewnames.csv | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename viewnames.csv => rowers/tests/viewnames.csv (100%) diff --git a/viewnames.csv b/rowers/tests/viewnames.csv similarity index 100% rename from viewnames.csv rename to rowers/tests/viewnames.csv From 7d54faf38a7595c690f90ca9375ae733ac640868 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 20 Jan 2020 17:08:32 +0100 Subject: [PATCH 46/80] small improvement in permissions tests --- rowers/tests/test_permissions2.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index e9c6c14a..7c99c220 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -221,6 +221,7 @@ class PermissionsViewTests(TestCase): if permissions['own'] and not permissions['is_staff']: urlstotest = [] falseurlstotest = [] + permissiondeniedtests = [] if permissions['own'] == 'basic': thisuser = self.ubasic notuser = None @@ -246,18 +247,24 @@ class PermissionsViewTests(TestCase): elif permissions['userid']: url = reverse(view,kwargs={'userid':thisuser['user'].id}) urlstotest.append(url) - if notuser: - url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) - falseurlstotest.append(url) + + url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) + permissiondeniedtests.append(url) for url in urlstotest: - print(url) - login = self.c.login(username = thisuser['username'],password = thisuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code, permissions['own_response']) + print(url) + login = self.c.login(username = thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['own_response']) for url in falseurlstotest: - print(url) - login = self.c.login(username = notuser['username'],password = notuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code, permissions['own_nonperm']) + print(url) + login = self.c.login(username = notuser['username'],password = notuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['own_nonperm']) + + for url in permissiondeniedtests: + print(url) + login = self.c.login(username = thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code,404) From 45808ec954be82bcd2eef765e578c1aaf48f15a1 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 20 Jan 2020 21:18:53 +0100 Subject: [PATCH 47/80] MOST! --- rowers/tests/test_permissions2.py | 137 ++++++- rowers/tests/viewnames.csv | 624 +++++++++++++++--------------- 2 files changed, 439 insertions(+), 322 deletions(-) diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index e9c6c14a..e3d0edef 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -146,6 +146,20 @@ class PermissionsViewTests(TestCase): notes=faker.text(), manager=self.ucoach['user']) + self.uplan['user'].rower.team.add(self.teamcoach) + self.ubasic['user'].rower.team.add(self.teamcoach) + self.ucoach['user'].rower.team.add(self.teamcoach) + + self.upro['user'].rower.team.add(self.teamplan) + self.uplan2['user'].rower.team.add(self.teamplan) + + self.upro['user'].rower.team.add(self.teampro) + self.uplan['user'].rower.team.add(self.teampro) + + self.uplan['user'].rower.coachinggroups.add(coachinggroup) + self.ubasic['user'].rower.coachinggroups.add(coachinggroup) + + viewstotest = [ ('workout_view', { @@ -187,7 +201,7 @@ class PermissionsViewTests(TestCase): - + # Test access for anonymous users @parameterized.expand(viewstotest) @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.read_df_sql') @@ -195,7 +209,7 @@ class PermissionsViewTests(TestCase): @patch('requests.get',side_effect=mocked_requests) @patch('requests.post',side_effect=mocked_requests) @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) - def test_permissions_generator( + def test_permissions_anonymous( self,view,permissions, mocked_sqlalchemy, mocked_read_df_sql, @@ -218,19 +232,40 @@ class PermissionsViewTests(TestCase): result = self.c.get(url) self.assertEqual(result.status_code,permissions['anonymous_response']) - if permissions['own'] and not permissions['is_staff']: + # Test access for logged in users - accessing own objects + @parameterized.expand(viewstotest) + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.read_df_sql') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('requests.get',side_effect=mocked_requests) + @patch('requests.post',side_effect=mocked_requests) + @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) + def test_permissions_own( + self,view,permissions, + mocked_sqlalchemy, + mocked_read_df_sql, + mocked_getsmallrowdata_db, + mock_get, + mock_post, + mocked_get_video_data, + ): + + if permissions['member'] and not permissions['is_staff']: urlstotest = [] falseurlstotest = [] - if permissions['own'] == 'basic': + otheruserurls = [] + + if permissions['member'] == 'basic': thisuser = self.ubasic + memberuser = self.uplan notuser = None - elif permissions['own'] == 'pro': + elif permissions['member'] == 'pro': thisuser = self.upro notuser = self.ubasic - elif permissions['own'] == 'plan': + elif permissions['member'] == 'plan': thisuser = self.uplan notuser = self.upro - elif permissions['own'] == 'coach': + elif permissions['member'] == 'coach': thisuser = self.ucoach notuser = self.uplan @@ -246,18 +281,100 @@ class PermissionsViewTests(TestCase): elif permissions['userid']: url = reverse(view,kwargs={'userid':thisuser['user'].id}) urlstotest.append(url) - if notuser: - url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) - falseurlstotest.append(url) + url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) + otheruserurls.append(url) + + # test logged in as user who has permissions for url in urlstotest: print(url) login = self.c.login(username = thisuser['username'],password = thisuser['password']) result = self.c.get(url) self.assertEqual(result.status_code, permissions['own_response']) + # test logged as user with no permissions (e.g. too low plan) for url in falseurlstotest: print(url) login = self.c.login(username = notuser['username'],password = notuser['password']) result = self.c.get(url) self.assertEqual(result.status_code, permissions['own_nonperm']) + + # test as user with permissions, accessing object of non-related user + for url in otheruserurls: + print(url) + login = self.c.login(username=thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code,403) + + # Test access for logged in users - accessing team member objects + @parameterized.expand(viewstotest) + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.read_df_sql') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('requests.get',side_effect=mocked_requests) + @patch('requests.post',side_effect=mocked_requests) + @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) + def test_permissions_own( + self,view,permissions, + mocked_sqlalchemy, + mocked_read_df_sql, + mocked_getsmallrowdata_db, + mock_get, + mock_post, + mocked_get_video_data, + ): + + if permissions['own'] and not permissions['is_staff']: + urlstotest = [] + falseurlstotest = [] + otheruserurls = [] + + if permissions['own'] == 'basic': + thisuser = self.ubasic + notuser = None + elif permissions['own'] == 'pro': + thisuser = self.upro + notuser = self.ubasic + elif permissions['own'] == 'plan': + thisuser = self.uplan + notuser = self.upro + elif permissions['own'] == 'coach': + thisuser = self.ucoach + notuser = self.uplan + + + if permissions['workoutid']: + workouts = thisuser['workouts'] + url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) + urlstotest.append(url) + if notuser: + workouts = notuser['workouts'] + url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) + falseurlstotest.append(url) + elif permissions['userid']: + url = reverse(view,kwargs={'userid':thisuser['user'].id}) + urlstotest.append(url) + + url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) + otheruserurls.append(url) + + # test logged in as user who has permissions + for url in urlstotest: + print(url) + login = self.c.login(username = thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['own_response']) + + # test logged as user with no permissions (e.g. too low plan) + for url in falseurlstotest: + print(url) + login = self.c.login(username = notuser['username'],password = notuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['own_nonperm']) + + # test as user with permissions, accessing object of non-related user + for url in otheruserurls: + print(url) + login = self.c.login(username=thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code,403) diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 9a62770a..9656f9ec 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -1,312 +1,312 @@ -,id,view,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid -0,0,workouts_summaries_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -1,1,rower_update_empower_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -2,2,agegroupcpview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -3,4,ajax_agegrouprecords,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -4,5,fitness_metric_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -5,6,agegrouprecordview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -6,7,workouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -7,8,virtualevents_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -8,9,virtualevent_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -9,10,indoorvirtualevent_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -10,11,virtualevent_toggle_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -11,12,indoorvirtualevent_toggle_email_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -12,13,virtualevent_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -13,14,virtualevent_ranking_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -14,15,virtualevent_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -15,16,indoorvirtualevent_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -16,17,virtualevent_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -17,18,indoorvirtualevent_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -18,19,virtualevent_addboat_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -19,20,virtualevent_withdraw_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -20,21,virtualevent_submit_result_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -21,22,virtualevent_disqualify_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -22,24,courses_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -23,25,course_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -24,26,addmanual_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -25,27,team_comparison_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,True -26,28,workouts_join_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -27,29,workouts_join_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -28,30,user_boxplot_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -29,31,analysis_new,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -30,32,user_multiflex_select,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -31,33,session_jobs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -32,34,session_jobs_status,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -33,35,kill_async_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -34,36,post_progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -35,37,graphs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -36,38,fitnessmetric_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -37,39,rankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -38,40,rankings_view2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -39,41,otwrankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -40,42,oterankings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -41,43,cum_flex,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -42,44,analysis_view_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -43,47,cum_flex_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -44,48,histo,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -45,49,histo_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -46,51,cumstats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -47,53,graph_show_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -48,54,GraphDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -49,55,get_thumbnails,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -50,56,otw_use_impeller,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -51,57,otw_use_gps,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -52,58,workout_toggle_ranking,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -53,59,team_workout_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -54,60,workout_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -55,61,workout_histo_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -56,62,workout_forcecurve_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -57,63,workout_unsubscribe_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -58,64,workout_comment_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -59,65,workout_tcxemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -60,66,workout_gpxemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -61,67,workout_csvemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -62,68,workout_csvtoadmin_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -63,69,workout_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -64,70,workout_map_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -65,71,workout_update_cp_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -66,72,instroke_chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -67,73,instroke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -68,74,workout_stats_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -69,75,workout_data_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -70,76,workout_otwsetpower_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -71,77,workout_otwpowerplot_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -72,78,workout_wind_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -73,79,workout_uploadimage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -74,80,virtualevent_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -75,81,virtualevent_uploadimage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -76,82,virtualevent_setlogo_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -77,83,logo_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -78,84,workout_downloadwind_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -79,85,workout_downloadmetar_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -80,86,workout_stream_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -81,87,workout_summary_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -82,88,workout_summary_restore_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -83,89,workout_split_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -84,90,workout_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -85,91,workout_video_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -86,92,VideoDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -87,93,workout_video_view_mini,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -88,94,workout_video_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -89,95,list_videos,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -90,96,video_selectworkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -91,98,workout_fusion_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -92,99,workout_fusion_list,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -93,100,TemplateView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -94,103,workout_add_chart_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -95,112,WorkoutDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -96,113,workout_smoothenpace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -97,114,workout_undo_smoothenpace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -98,115,workout_c2import_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -99,120,workout_stravaimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -100,122,workout_getc2workout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -101,124,workout_getimportview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -102,125,workout_getstravaworkout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -103,126,workout_getstravaworkout_next,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -104,127,workout_sporttracksimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -105,129,workout_getsporttracksworkout_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -106,130,workout_polarimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -107,132,workout_runkeeperimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -108,134,workout_underarmourimport_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -109,135,workout_c2_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -110,136,workout_strava_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -111,137,workout_recalcsummary_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -112,138,workout_sporttracks_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -113,139,workout_runkeeper_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -114,140,workout_underarmour_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -115,141,workout_tp_upload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -116,142,multi_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -117,145,alerts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -118,147,AlertDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -119,148,alert_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -120,150,alert_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -121,152,alert_report_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -122,155,boxplot_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -123,157,boxplot_view_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -124,158,multiflex_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -125,160,multiflex_data,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -126,161,deactivate_user,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -127,162,remove_user,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -128,163,user_gdpr_confirm,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -129,165,user_gdpr_optin,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -130,167,rower_teams_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -131,168,rower_calcdps_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -132,169,rower_exportsettings_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -133,171,team_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -134,172,team_members_stats_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -135,173,team_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -136,174,team_leaveconfirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -137,175,team_leave_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -138,176,team_deleteconfirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -139,177,team_requestmembership_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -140,178,reject_revoke_coach_request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -141,179,coach_drop_athlete_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -142,180,coach_drop_athlete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -143,181,athlete_drop_coach_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -144,182,athlete_drop_coach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -145,184,reject_revoke_coach_offer,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -146,186,request_coaching_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -147,187,offer_coaching_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -148,188,coach_accept_coachrequest_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -149,189,rower_accept_coachoffer_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -150,190,team_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -151,191,team_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -152,192,manager_member_drop_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -153,193,invitation_reject_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -154,194,invitation_revoke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -155,195,rower_invitations_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -156,196,raise_500,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -157,198,request_revoke_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -158,199,request_reject_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -159,200,manager_requests_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -160,202,rower_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -161,204,rower_prefs_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -162,205,transactions_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -163,208,rower_c2_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -164,209,rower_polar_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -165,210,rower_revokeapp_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -166,211,rower_strava_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -167,212,rower_sporttracks_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -168,213,rower_underarmour_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -169,214,rower_tp_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -170,215,rower_runkeeper_authorize,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -171,216,rower_sporttracks_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -172,217,rower_underarmour_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -173,218,rower_tp_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -174,219,rower_c2_token_refresh,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -175,220,rower_favoritecharts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -176,222,workout_workflow_config2_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -177,224,workflow_default_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -178,225,sendmail,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -179,231,analysis_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -180,232,laboratory_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -181,233,errormessage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -182,237,payment_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -183,238,upgrade_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -184,240,downgrade_confirm_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -185,241,billing_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -186,242,upgrade_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -187,243,downgrade_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -188,244,payment_completed_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -189,245,downgrade_completed_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -190,246,paidplans_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -191,247,plan_stop_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -192,248,plan_tobasic_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -193,249,checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -194,250,upgrade_checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -195,251,downgrade_checkouts_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -196,252,planrequired_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -197,253,start_trial_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -198,254,start_plantrial_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -199,256,rower_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -200,257,freecoach_register_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -201,259,workout_workflow_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -202,260,workout_flexchart3_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -203,264,rower_process_testcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -204,265,rower_create_trainingplan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -205,267,TrainingPlanDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -206,268,MicroCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -207,269,MesoCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -208,270,MacroCycleDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -209,271,rower_trainingplan_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -210,279,rower_trainingplan_execution_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -211,281,TrainingMacroCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -212,282,TrainingMesoCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -213,283,planmacrocyclebymonth,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -214,285,planmesocyclebyweek,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -215,287,TrainingMicroCycleUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -216,288,rower_delete_trainingtarget,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -217,289,TrainingPlanUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -218,290,TrainingTargetUpdate,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -219,291,strokedataform,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -220,292,plannedsession_teamcreate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -221,296,plannedsession_teamedit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -222,298,plannedsession_create_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -223,300,plannedsession_multiclone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -224,302,plannedsession_multicreate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -225,305,plannedsession_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -226,306,plannedsession_totemplate_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -227,307,plannedsession_compare_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -228,310,plannedsession_clone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -229,311,plannedsession_teamclone_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -230,313,plannedsession_detach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -231,315,plannedsession_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -232,317,PlannedSessionDelete,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -233,319,plannedsessions_manage_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -234,323,plannedsessions_coach_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -235,325,plannedsessions_coach_icsemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -236,326,plannedsessions_print_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -237,327,plannedsession_comment_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -238,330,plannedsessions_icsemail_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -239,332,plannedsessions_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -240,334,course_edit_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -241,335,course_delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -242,336,course_kmldownload_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -243,337,course_replace_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -244,338,course_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -245,339,course_map_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -246,341,c2listdebug_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -247,343,rower_process_callback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -248,344,rower_process_stravacallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -249,345,rower_process_sporttrackscallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -250,346,rower_process_underarmourcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -251,347,rower_process_polarcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -252,348,rower_process_runkeepercallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -253,349,rower_process_tpcallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -254,350,rower_process_twittercallback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -255,351,set_language,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -256,352,SetOffsetView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -257,353,stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -258,354,stats_json,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -259,355,jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -260,356,workers,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -261,357,worker_details,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -262,358,finished_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -263,359,started_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -264,360,deferred_jobs,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -265,361,clear_queue,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -266,362,requeue_all,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -267,363,job_detail,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -268,364,delete_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -269,365,actions,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -270,366,requeue_job_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -271,367,enqueue_job,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -272,368,JavaScriptCatalog,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -273,369,serve,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -274,371,LoginView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -275,372,LogoutView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -276,373,PasswordChangeView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -277,374,PasswordChangeDoneView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -278,375,PasswordResetView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -279,376,PasswordResetDoneView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -280,377,PasswordResetConfirmView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -281,378,PasswordResetCompleteView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -282,386,index,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -283,387,login,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -284,388,logout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -285,389,password_change,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -286,390,password_change_done,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -287,391,i18n_javascript,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -288,392,shortcut,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -289,393,changelist_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -290,394,add_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -291,395,autocomplete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -292,396,history_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -293,397,delete_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -294,398,change_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -295,399,RedirectView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -296,568,app_index,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -297,571,landingview,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -298,573,get_blog_posts,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -299,576,AuthorizationView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -300,577,TokenView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -301,578,WorkoutViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -302,582,RowerViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -303,586,FavoriteChartViewSet,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -304,590,APIRootView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -305,592,SwaggerSchemaView,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -306,595,strokedatajson,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -307,596,error500_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -308,603,failed_queue_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -309,604,failed_queue_empty,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -310,605,failed_job_view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid +0,0,workouts_summaries_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False +1,1,rower_update_empower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +2,2,agegroupcpview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +3,4,ajax_agegrouprecords,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +4,5,fitness_metric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +5,6,agegrouprecordview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +6,7,workouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +7,8,virtualevents_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +8,9,virtualevent_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +9,10,indoorvirtualevent_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +10,11,virtualevent_toggle_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +11,12,indoorvirtualevent_toggle_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +12,13,virtualevent_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +13,14,virtualevent_ranking_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +14,15,virtualevent_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +15,16,indoorvirtualevent_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +16,17,virtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +17,18,indoorvirtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +18,19,virtualevent_addboat_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +19,20,virtualevent_withdraw_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +20,21,virtualevent_submit_result_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +21,22,virtualevent_disqualify_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +22,24,courses_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +23,25,course_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +24,26,addmanual_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +25,27,team_comparison_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,True +26,28,workouts_join_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +27,29,workouts_join_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +28,30,user_boxplot_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +29,31,analysis_new,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +30,32,user_multiflex_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +31,33,session_jobs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +32,34,session_jobs_status,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +33,35,kill_async_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +34,36,post_progress,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +35,37,graphs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +36,38,fitnessmetric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +37,39,rankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +38,40,rankings_view2,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +39,41,otwrankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +40,42,oterankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +41,43,cum_flex,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +42,44,analysis_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +43,47,cum_flex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +44,48,histo,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +45,49,histo_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +46,51,cumstats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +47,53,graph_show_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +48,54,GraphDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +49,55,get_thumbnails,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +50,56,otw_use_impeller,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +51,57,otw_use_gps,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +52,58,workout_toggle_ranking,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +53,59,team_workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +54,60,workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +55,61,workout_histo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +56,62,workout_forcecurve_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +57,63,workout_unsubscribe_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +58,64,workout_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +59,65,workout_tcxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +60,66,workout_gpxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +61,67,workout_csvemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +62,68,workout_csvtoadmin_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +63,69,workout_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +64,70,workout_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +65,71,workout_update_cp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +66,72,instroke_chart,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +67,73,instroke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +68,74,workout_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +69,75,workout_data_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +70,76,workout_otwsetpower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +71,77,workout_otwpowerplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +72,78,workout_wind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +73,79,workout_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +74,80,virtualevent_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +75,81,virtualevent_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +76,82,virtualevent_setlogo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +77,83,logo_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +78,84,workout_downloadwind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +79,85,workout_downloadmetar_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +80,86,workout_stream_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +81,87,workout_summary_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +82,88,workout_summary_restore_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +83,89,workout_split_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +84,90,workout_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +85,91,workout_video_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +86,92,VideoDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +87,93,workout_video_view_mini,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +88,94,workout_video_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +89,95,list_videos,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +90,96,video_selectworkout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +91,98,workout_fusion_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +92,99,workout_fusion_list,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +93,100,TemplateView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +94,103,workout_add_chart_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +95,112,WorkoutDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +96,113,workout_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +97,114,workout_undo_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +98,115,workout_c2import_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +99,120,workout_stravaimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +100,122,workout_getc2workout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +101,124,workout_getimportview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +102,125,workout_getstravaworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +103,126,workout_getstravaworkout_next,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +104,127,workout_sporttracksimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +105,129,workout_getsporttracksworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +106,130,workout_polarimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +107,132,workout_runkeeperimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +108,134,workout_underarmourimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +109,135,workout_c2_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +110,136,workout_strava_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +111,137,workout_recalcsummary_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +112,138,workout_sporttracks_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +113,139,workout_runkeeper_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +114,140,workout_underarmour_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +115,141,workout_tp_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +116,142,multi_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +119,148,alert_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +120,150,alert_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +121,152,alert_report_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +122,155,boxplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +123,157,boxplot_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +124,158,multiflex_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +125,160,multiflex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +126,161,deactivate_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +127,162,remove_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +128,163,user_gdpr_confirm,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +129,165,user_gdpr_optin,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +130,167,rower_teams_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +131,168,rower_calcdps_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +132,169,rower_exportsettings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +133,171,team_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +134,172,team_members_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +135,173,team_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +136,174,team_leaveconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +137,175,team_leave_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +138,176,team_deleteconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +139,177,team_requestmembership_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +140,178,reject_revoke_coach_request,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +141,179,coach_drop_athlete_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +142,180,coach_drop_athlete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +143,181,athlete_drop_coach_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +144,182,athlete_drop_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +145,184,reject_revoke_coach_offer,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +146,186,request_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +147,187,offer_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +148,188,coach_accept_coachrequest_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +149,189,rower_accept_coachoffer_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +150,190,team_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +151,191,team_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +152,192,manager_member_drop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +153,193,invitation_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +154,194,invitation_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +155,195,rower_invitations_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +156,196,raise_500,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +157,198,request_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +158,199,request_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +159,200,manager_requests_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +160,202,rower_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +161,204,rower_prefs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False +240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False +310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True From f505fbb74e7732d69d0af2756e695cf4fef428ca Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 20 Jan 2020 21:46:52 +0100 Subject: [PATCH 48/80] added tests for coach --- rowers/tests/test_permissions2.py | 99 ++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index e3d0edef..054aed95 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -149,6 +149,7 @@ class PermissionsViewTests(TestCase): self.uplan['user'].rower.team.add(self.teamcoach) self.ubasic['user'].rower.team.add(self.teamcoach) self.ucoach['user'].rower.team.add(self.teamcoach) + self.upro['user'].rower.team.add(self.teamcoach) self.upro['user'].rower.team.add(self.teamplan) self.uplan2['user'].rower.team.add(self.teamplan) @@ -294,10 +295,10 @@ class PermissionsViewTests(TestCase): # test logged as user with no permissions (e.g. too low plan) for url in falseurlstotest: - print(url) - login = self.c.login(username = notuser['username'],password = notuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code, permissions['own_nonperm']) + print(url) + login = self.c.login(username = notuser['username'],password = notuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['own_nonperm']) # test as user with permissions, accessing object of non-related user for url in otheruserurls: @@ -314,7 +315,7 @@ class PermissionsViewTests(TestCase): @patch('requests.get',side_effect=mocked_requests) @patch('requests.post',side_effect=mocked_requests) @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) - def test_permissions_own( + def test_permissions_member( self,view,permissions, mocked_sqlalchemy, mocked_read_df_sql, @@ -329,27 +330,29 @@ class PermissionsViewTests(TestCase): falseurlstotest = [] otheruserurls = [] - if permissions['own'] == 'basic': + if permissions['member'] == 'basic': thisuser = self.ubasic + memberuser = self.uplan notuser = None - elif permissions['own'] == 'pro': + elif permissions['member'] == 'pro': thisuser = self.upro + memberuser = self.uplan notuser = self.ubasic - elif permissions['own'] == 'plan': + elif permissions['member'] == 'plan': thisuser = self.uplan + memberuser = self.ubasic notuser = self.upro - elif permissions['own'] == 'coach': + elif permissions['member'] == 'coach': thisuser = self.ucoach + memberuser = self.uplan notuser = self.uplan if permissions['workoutid']: - workouts = thisuser['workouts'] + workouts = memberuser['workouts'] url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) urlstotest.append(url) if notuser: - workouts = notuser['workouts'] - url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) falseurlstotest.append(url) elif permissions['userid']: url = reverse(view,kwargs={'userid':thisuser['user'].id}) @@ -360,17 +363,73 @@ class PermissionsViewTests(TestCase): # test logged in as user who has permissions for url in urlstotest: - print(url) - login = self.c.login(username = thisuser['username'],password = thisuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code, permissions['own_response']) + print(url) + login = self.c.login(username = thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['member_response']) # test logged as user with no permissions (e.g. too low plan) for url in falseurlstotest: - print(url) - login = self.c.login(username = notuser['username'],password = notuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code, permissions['own_nonperm']) + print(url) + login = self.c.login(username = notuser['username'],password = notuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['member_nonperm']) + + # test as user with permissions, accessing object of non-related user + for url in otheruserurls: + print(url) + login = self.c.login(username=thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code,403) + + # Test access for logged in users - accessing coachee + @parameterized.expand(viewstotest) + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.read_df_sql') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('requests.get',side_effect=mocked_requests) + @patch('requests.post',side_effect=mocked_requests) + @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) + def test_permissions_member( + self,view,permissions, + mocked_sqlalchemy, + mocked_read_df_sql, + mocked_getsmallrowdata_db, + mock_get, + mock_post, + mocked_get_video_data, + ): + + if permissions['own'] and not permissions['is_staff']: + urlstotest = [] + falseurlstotest = [] + otheruserurls = [] + + if permissions['coachee'] == 'coach': + thisuser = self.ucoach + coacheeuser = self.ubasic + notuser = self.uplan + + + + if permissions['workoutid']: + workouts = coacheeuser['workouts'] + url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) + urlstotest.append(url) + + elif permissions['userid']: + url = reverse(view,kwargs={'userid':coacheeuser['user'].id}) + urlstotest.append(url) + + url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) + otheruserurls.append(url) + + # test logged in as user who has permissions + for url in urlstotest: + print(url) + login = self.c.login(username = thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['coachee_response']) # test as user with permissions, accessing object of non-related user for url in otheruserurls: From 5d37acbbc888d7d0d3b73cecc18849dceb91608d Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 21 Jan 2020 16:17:09 +0100 Subject: [PATCH 49/80] first working version with viewnames.csv --- rowers/tests/test_permissions2.py | 216 +++++++---- rowers/tests/viewnames.csv | 624 +++++++++++++++--------------- 2 files changed, 443 insertions(+), 397 deletions(-) diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index 25e0f858..7fd0b1da 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -178,6 +178,7 @@ class PermissionsViewTests(TestCase): 'is_staff': False, 'workoutid': True, 'userid': False, + 'dotest': True, } ), ('workouts_view', @@ -196,11 +197,39 @@ class PermissionsViewTests(TestCase): 'is_staff': False, 'workoutid': False, 'userid': True, + 'dotest': True, } ) ] + import pandas as pd + df = pd.read_csv('./rowers/tests/viewnames.csv') + for id, row in df.iterrows(): + view = row['view'] + tpl = (view, + { + 'anonymous':row.anonymous, + 'anonymous_response':row.anonymous_response, + 'own': row.own, + 'own_response':row.own_response, + 'own_nonperm': row.own_nonperm, + 'member': row.member, + 'member_response':row.member_response, + 'member_nonperm': row.member_nonperm, + 'coachee': row.coachee, + 'coachee_response':row.coachee_response, + 'coachee_nonperm': row.coachee_nonperm, + 'is_staff':row.is_staff, + 'workoutid':row.workoutid, + 'userid':row.userid, + 'dotest':row.dotest, + }) + if row.dotest: + viewstotest.append(tpl) + + + plans = ['basic','plan','coach','pro'] # Test access for anonymous users @parameterized.expand(viewstotest) @@ -220,13 +249,14 @@ class PermissionsViewTests(TestCase): mocked_get_video_data, ): - if permissions['anonymous'] and not permissions['is_staff']: + if permissions['anonymous'] in plans and not permissions['is_staff'] and permissions['dotest']: urlstotest = [] if permissions['workoutid']: url = reverse(view,kwargs={'id':encoder.encode_hex(self.ucoach['workouts'][0].id)}) urlstotest.append(url) else: url = reverse(view) + urlstotest.append(url) for url in urlstotest: print(url) @@ -251,22 +281,26 @@ class PermissionsViewTests(TestCase): mocked_get_video_data, ): - if permissions['member'] and not permissions['is_staff']: + if permissions['own'] in plans and not permissions['is_staff'] and permissions['dotest']: urlstotest = [] falseurlstotest = [] otheruserurls = [] - if permissions['member'] == 'basic': + thisuser = self.ubasic + memberuser = self.uplan + notuser = None + + if permissions['own'] == 'basic': thisuser = self.ubasic memberuser = self.uplan notuser = None - elif permissions['member'] == 'pro': + elif permissions['own'] == 'pro': thisuser = self.upro notuser = self.ubasic - elif permissions['member'] == 'plan': + elif permissions['own'] == 'plan': thisuser = self.uplan notuser = self.upro - elif permissions['member'] == 'coach': + elif permissions['own'] == 'coach': thisuser = self.ucoach notuser = self.uplan @@ -285,6 +319,9 @@ class PermissionsViewTests(TestCase): url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) otheruserurls.append(url) + else: + url = reverse(view) + urlstotest.append(url) # test logged in as user who has permissions for url in urlstotest: @@ -325,62 +362,69 @@ class PermissionsViewTests(TestCase): mocked_get_video_data, ): - if permissions['own'] and not permissions['is_staff']: - urlstotest = [] - falseurlstotest = [] - otheruserurls = [] + if permissions['member'] in plans and not permissions['is_staff'] and permissions['dotest']: + urlstotest = [] + falseurlstotest = [] + otheruserurls = [] - if permissions['member'] == 'basic': - thisuser = self.ubasic - memberuser = self.uplan - notuser = None - elif permissions['member'] == 'pro': - thisuser = self.upro - memberuser = self.uplan - notuser = self.ubasic - elif permissions['member'] == 'plan': - thisuser = self.uplan - memberuser = self.ubasic - notuser = self.upro - elif permissions['member'] == 'coach': - thisuser = self.ucoach - memberuser = self.uplan - notuser = self.uplan + thisuser = self.ubasic + memberuser = self.uplan + notuser = None + + if permissions['member'] == 'basic': + thisuser = self.ubasic + memberuser = self.uplan + notuser = None + elif permissions['member'] == 'pro': + thisuser = self.upro + memberuser = self.uplan + notuser = self.ubasic + elif permissions['member'] == 'plan': + thisuser = self.uplan + memberuser = self.ubasic + notuser = self.upro + elif permissions['member'] == 'coach': + thisuser = self.ucoach + memberuser = self.uplan + notuser = self.uplan - if permissions['workoutid']: - workouts = memberuser['workouts'] - url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) - urlstotest.append(url) - if notuser: - falseurlstotest.append(url) - elif permissions['userid']: - url = reverse(view,kwargs={'userid':thisuser['user'].id}) - urlstotest.append(url) + if permissions['workoutid']: + workouts = memberuser['workouts'] + url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) + urlstotest.append(url) + if notuser: + falseurlstotest.append(url) + elif permissions['userid']: + url = reverse(view,kwargs={'userid':thisuser['user'].id}) + urlstotest.append(url) - url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) - otheruserurls.append(url) + url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) + otheruserurls.append(url) + else: + url = reverse(view) + urlstotest.append(url) - # test logged in as user who has permissions - for url in urlstotest: - print(url) - login = self.c.login(username = thisuser['username'],password = thisuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code, permissions['member_response']) + # test logged in as user who has permissions + for url in urlstotest: + print(url) + login = self.c.login(username = thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['member_response']) - # test logged as user with no permissions (e.g. too low plan) - for url in falseurlstotest: - print(url) - login = self.c.login(username = notuser['username'],password = notuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code, permissions['member_nonperm']) + # test logged as user with no permissions (e.g. too low plan) + for url in falseurlstotest: + print(url) + login = self.c.login(username = notuser['username'],password = notuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['member_nonperm']) - # test as user with permissions, accessing object of non-related user - for url in otheruserurls: - print(url) - login = self.c.login(username=thisuser['username'],password = thisuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code,403) + # test as user with permissions, accessing object of non-related user + for url in otheruserurls: + print(url) + login = self.c.login(username=thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code,403) # Test access for logged in users - accessing coachee @parameterized.expand(viewstotest) @@ -390,7 +434,7 @@ class PermissionsViewTests(TestCase): @patch('requests.get',side_effect=mocked_requests) @patch('requests.post',side_effect=mocked_requests) @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) - def test_permissions_member( + def test_permissions_coachee( self,view,permissions, mocked_sqlalchemy, mocked_read_df_sql, @@ -400,40 +444,42 @@ class PermissionsViewTests(TestCase): mocked_get_video_data, ): - if permissions['own'] and not permissions['is_staff']: - urlstotest = [] - falseurlstotest = [] - otheruserurls = [] + if permissions['coachee'] in plans and not permissions['is_staff'] and permissions['dotest']: + urlstotest = [] + falseurlstotest = [] + otheruserurls = [] - if permissions['coachee'] == 'coach': - thisuser = self.ucoach - coacheeuser = self.ubasic - notuser = self.uplan + thisuser = self.ucoach + coacheeuser = self.ubasic + notuser = self.uplan - if permissions['workoutid']: - workouts = coacheeuser['workouts'] - url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) - urlstotest.append(url) + if permissions['workoutid']: + workouts = coacheeuser['workouts'] + url = reverse(view,kwargs={'id':encoder.encode_hex(workouts[0].id)}) + urlstotest.append(url) - elif permissions['userid']: - url = reverse(view,kwargs={'userid':coacheeuser['user'].id}) - urlstotest.append(url) + elif permissions['userid']: + url = reverse(view,kwargs={'userid':coacheeuser['user'].id}) + urlstotest.append(url) - url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) - otheruserurls.append(url) + url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) + otheruserurls.append(url) + else: + url = reverse(view) + urlstotest.append(url) - # test logged in as user who has permissions - for url in urlstotest: - print(url) - login = self.c.login(username = thisuser['username'],password = thisuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code, permissions['coachee_response']) + # test logged in as user who has permissions + for url in urlstotest: + print(url) + login = self.c.login(username = thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code, permissions['coachee_response']) - # test as user with permissions, accessing object of non-related user - for url in otheruserurls: - print(url) - login = self.c.login(username=thisuser['username'],password = thisuser['password']) - result = self.c.get(url) - self.assertEqual(result.status_code,403) + # test as user with permissions, accessing object of non-related user + for url in otheruserurls: + print(url) + login = self.c.login(username=thisuser['username'],password = thisuser['password']) + result = self.c.get(url) + self.assertEqual(result.status_code,403) diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 9656f9ec..cd3e5763 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -1,312 +1,312 @@ -,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid -0,0,workouts_summaries_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,False,False,False -1,1,rower_update_empower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -2,2,agegroupcpview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -3,4,ajax_agegrouprecords,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -4,5,fitness_metric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -5,6,agegrouprecordview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -6,7,workouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -7,8,virtualevents_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -8,9,virtualevent_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -9,10,indoorvirtualevent_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -10,11,virtualevent_toggle_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -11,12,indoorvirtualevent_toggle_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -12,13,virtualevent_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -13,14,virtualevent_ranking_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -14,15,virtualevent_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -15,16,indoorvirtualevent_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -16,17,virtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -17,18,indoorvirtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -18,19,virtualevent_addboat_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -19,20,virtualevent_withdraw_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -20,21,virtualevent_submit_result_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -21,22,virtualevent_disqualify_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -22,24,courses_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -23,25,course_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -24,26,addmanual_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -25,27,team_comparison_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,True -26,28,workouts_join_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -27,29,workouts_join_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -28,30,user_boxplot_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -29,31,analysis_new,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -30,32,user_multiflex_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -31,33,session_jobs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -32,34,session_jobs_status,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -33,35,kill_async_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -34,36,post_progress,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -35,37,graphs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -36,38,fitnessmetric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -37,39,rankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -38,40,rankings_view2,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -39,41,otwrankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -40,42,oterankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -41,43,cum_flex,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -42,44,analysis_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -43,47,cum_flex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -44,48,histo,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -45,49,histo_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -46,51,cumstats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -47,53,graph_show_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -48,54,GraphDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -49,55,get_thumbnails,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -50,56,otw_use_impeller,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -51,57,otw_use_gps,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -52,58,workout_toggle_ranking,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -53,59,team_workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -54,60,workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -55,61,workout_histo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -56,62,workout_forcecurve_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -57,63,workout_unsubscribe_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -58,64,workout_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -59,65,workout_tcxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -60,66,workout_gpxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -61,67,workout_csvemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -62,68,workout_csvtoadmin_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -63,69,workout_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -64,70,workout_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -65,71,workout_update_cp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -66,72,instroke_chart,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -67,73,instroke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -68,74,workout_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -69,75,workout_data_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -70,76,workout_otwsetpower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -71,77,workout_otwpowerplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -72,78,workout_wind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -73,79,workout_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -74,80,virtualevent_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -75,81,virtualevent_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -76,82,virtualevent_setlogo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -77,83,logo_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -78,84,workout_downloadwind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -79,85,workout_downloadmetar_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -80,86,workout_stream_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -81,87,workout_summary_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -82,88,workout_summary_restore_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -83,89,workout_split_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -84,90,workout_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -85,91,workout_video_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -86,92,VideoDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -87,93,workout_video_view_mini,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -88,94,workout_video_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -89,95,list_videos,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -90,96,video_selectworkout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -91,98,workout_fusion_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -92,99,workout_fusion_list,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -93,100,TemplateView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -94,103,workout_add_chart_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -95,112,WorkoutDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -96,113,workout_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -97,114,workout_undo_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -98,115,workout_c2import_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -99,120,workout_stravaimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -100,122,workout_getc2workout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -101,124,workout_getimportview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -102,125,workout_getstravaworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -103,126,workout_getstravaworkout_next,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -104,127,workout_sporttracksimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -105,129,workout_getsporttracksworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -106,130,workout_polarimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -107,132,workout_runkeeperimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -108,134,workout_underarmourimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -109,135,workout_c2_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -110,136,workout_strava_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -111,137,workout_recalcsummary_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -112,138,workout_sporttracks_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -113,139,workout_runkeeper_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -114,140,workout_underarmour_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -115,141,workout_tp_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -116,142,multi_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -119,148,alert_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -120,150,alert_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -121,152,alert_report_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -122,155,boxplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -123,157,boxplot_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -124,158,multiflex_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -125,160,multiflex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -126,161,deactivate_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -127,162,remove_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -128,163,user_gdpr_confirm,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -129,165,user_gdpr_optin,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -130,167,rower_teams_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -131,168,rower_calcdps_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -132,169,rower_exportsettings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -133,171,team_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -134,172,team_members_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -135,173,team_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -136,174,team_leaveconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -137,175,team_leave_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -138,176,team_deleteconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -139,177,team_requestmembership_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -140,178,reject_revoke_coach_request,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -141,179,coach_drop_athlete_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -142,180,coach_drop_athlete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -143,181,athlete_drop_coach_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -144,182,athlete_drop_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -145,184,reject_revoke_coach_offer,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -146,186,request_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -147,187,offer_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -148,188,coach_accept_coachrequest_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -149,189,rower_accept_coachoffer_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -150,190,team_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -151,191,team_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -152,192,manager_member_drop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -153,193,invitation_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -154,194,invitation_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -155,195,rower_invitations_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -156,196,raise_500,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -157,198,request_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -158,199,request_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -159,200,manager_requests_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -160,202,rower_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -161,204,rower_prefs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,True,False -240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True -307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,False -310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,True,False,True +,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest +0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,404,404,FALSE,404,404,FALSE,FALSE,FALSE,TRUE +1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,TRUE,FALSE,FALSE,TRUE +2,2,agegroupcpview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +3,4,ajax_agegrouprecords,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +4,5,fitness_metric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +5,6,agegrouprecordview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +6,7,workouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +7,8,virtualevents_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +8,9,virtualevent_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +9,10,indoorvirtualevent_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +10,11,virtualevent_toggle_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +11,12,indoorvirtualevent_toggle_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +12,13,virtualevent_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +13,14,virtualevent_ranking_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +14,15,virtualevent_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +15,16,indoorvirtualevent_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +16,17,virtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +17,18,indoorvirtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +18,19,virtualevent_addboat_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +19,20,virtualevent_withdraw_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +20,21,virtualevent_submit_result_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +21,22,virtualevent_disqualify_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +22,24,courses_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +23,25,course_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +24,26,addmanual_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +25,27,team_comparison_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,TRUE,FALSE +26,28,workouts_join_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +27,29,workouts_join_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +28,30,user_boxplot_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +29,31,analysis_new,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +30,32,user_multiflex_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +31,33,session_jobs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +32,34,session_jobs_status,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +33,35,kill_async_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +34,36,post_progress,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +35,37,graphs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +36,38,fitnessmetric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +37,39,rankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +38,40,rankings_view2,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +39,41,otwrankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +40,42,oterankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +41,43,cum_flex,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +42,44,analysis_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +43,47,cum_flex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +44,48,histo,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +45,49,histo_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +46,51,cumstats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +47,53,graph_show_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +48,54,GraphDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +49,55,get_thumbnails,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +50,56,otw_use_impeller,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +51,57,otw_use_gps,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +52,58,workout_toggle_ranking,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +53,59,team_workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +54,60,workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +55,61,workout_histo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +56,62,workout_forcecurve_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +57,63,workout_unsubscribe_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +58,64,workout_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +59,65,workout_tcxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +60,66,workout_gpxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +61,67,workout_csvemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +62,68,workout_csvtoadmin_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +63,69,workout_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +64,70,workout_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +65,71,workout_update_cp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +66,72,instroke_chart,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +67,73,instroke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +68,74,workout_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +69,75,workout_data_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +70,76,workout_otwsetpower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +71,77,workout_otwpowerplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +72,78,workout_wind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +73,79,workout_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +74,80,virtualevent_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +75,81,virtualevent_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +76,82,virtualevent_setlogo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +77,83,logo_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +78,84,workout_downloadwind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +79,85,workout_downloadmetar_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +80,86,workout_stream_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +81,87,workout_summary_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +82,88,workout_summary_restore_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +83,89,workout_split_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +84,90,workout_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +85,91,workout_video_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +86,92,VideoDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +87,93,workout_video_view_mini,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +88,94,workout_video_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +89,95,list_videos,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +90,96,video_selectworkout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +91,98,workout_fusion_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +92,99,workout_fusion_list,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +93,100,TemplateView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +94,103,workout_add_chart_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +95,112,WorkoutDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +96,113,workout_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +97,114,workout_undo_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +98,115,workout_c2import_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +99,120,workout_stravaimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +100,122,workout_getc2workout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +101,124,workout_getimportview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +102,125,workout_getstravaworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +103,126,workout_getstravaworkout_next,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +104,127,workout_sporttracksimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +105,129,workout_getsporttracksworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +106,130,workout_polarimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +107,132,workout_runkeeperimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +108,134,workout_underarmourimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +109,135,workout_c2_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +110,136,workout_strava_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +111,137,workout_recalcsummary_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +112,138,workout_sporttracks_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +113,139,workout_runkeeper_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +114,140,workout_underarmour_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +115,141,workout_tp_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +116,142,multi_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +119,148,alert_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +120,150,alert_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +121,152,alert_report_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +122,155,boxplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +123,157,boxplot_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +124,158,multiflex_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +125,160,multiflex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +126,161,deactivate_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +127,162,remove_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +128,163,user_gdpr_confirm,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +129,165,user_gdpr_optin,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +130,167,rower_teams_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +131,168,rower_calcdps_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +132,169,rower_exportsettings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +133,171,team_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +134,172,team_members_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +135,173,team_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +136,174,team_leaveconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +137,175,team_leave_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +138,176,team_deleteconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +139,177,team_requestmembership_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +140,178,reject_revoke_coach_request,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +141,179,coach_drop_athlete_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +142,180,coach_drop_athlete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +143,181,athlete_drop_coach_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +144,182,athlete_drop_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +145,184,reject_revoke_coach_offer,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +146,186,request_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +147,187,offer_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +148,188,coach_accept_coachrequest_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +149,189,rower_accept_coachoffer_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +150,190,team_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +151,191,team_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +152,192,manager_member_drop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +153,193,invitation_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +154,194,invitation_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +155,195,rower_invitations_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +156,196,raise_500,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +157,198,request_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +158,199,request_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +159,200,manager_requests_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +160,202,rower_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +161,204,rower_prefs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE +240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE From 9db68af18905c2d64a875fa94d12b3dfdd6f1da7 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 21 Jan 2020 17:30:23 +0100 Subject: [PATCH 50/80] added some comments in csv --- rowers/tests/viewnames.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index cd3e5763..5889fe8e 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -1,8 +1,8 @@ ,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest 0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,404,404,FALSE,404,404,FALSE,FALSE,FALSE,TRUE 1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,TRUE,FALSE,FALSE,TRUE -2,2,agegroupcpview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -3,4,ajax_agegrouprecords,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE +3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE 4,5,fitness_metric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE 5,6,agegrouprecordview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE 6,7,workouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE From fa39b05319bd9e31937702fdb30ace2ee965e3c0 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 22 Jan 2020 18:45:53 +0100 Subject: [PATCH 51/80] working on permissions_2 --- rowers/tests/test_permissions2.py | 160 ++++---- rowers/tests/viewnames.csv | 620 +++++++++++++++--------------- rowers/urls.py | 2 + 3 files changed, 392 insertions(+), 390 deletions(-) diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index 7fd0b1da..bc2e6b36 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -1,5 +1,56 @@ from .statements import * +viewstotest = [ + ('workout_view', + { + 'anonymous': True, + 'anonymous_response':200, + 'own': 'basic', + 'own_response': 200, + 'own_nonperm': 302, + 'member': 'basic', + 'member_response': 200, + 'member_nonperm': 404, + 'coachee': 'coach', + 'coachee_response': 200, + 'coachee_nonperm': 404, + 'is_staff': False, + 'workoutid': True, + 'userid': False, + 'dotest': True, + } + ), +] + +import pandas as pd +df = pd.read_csv('./rowers/tests/viewnames.csv') + +for id, row in df.iterrows(): + view = row['view'] + tpl = (view, + { + 'anonymous':row.anonymous, + 'anonymous_response':row.anonymous_response, + 'own': row.own, + 'own_response':row.own_response, + 'own_nonperm': row.own_nonperm, + 'member': row.member, + 'member_response':row.member_response, + 'member_nonperm': row.member_nonperm, + 'coachee': row.coachee, + 'coachee_response':row.coachee_response, + 'coachee_nonperm': row.coachee_nonperm, + 'is_staff':row.is_staff, + 'workoutid':row.workoutid, + 'userid':row.userid, + 'dotest':row.dotest, + }) + if row.dotest: + viewstotest.append(tpl) + + +plans = ['basic','plan','coach','pro'] + @override_settings(TESTING=True) class PermissionsViewTests(TestCase): def setUp(self): @@ -161,76 +212,6 @@ class PermissionsViewTests(TestCase): self.ubasic['user'].rower.coachinggroups.add(coachinggroup) - viewstotest = [ - ('workout_view', - { - 'anonymous': True, - 'anonymous_response':200, - 'own': 'basic', - 'own_response': 200, - 'own_nonperm': 302, - 'member': 'basic', - 'member_response': 200, - 'member_nonperm': 404, - 'coachee': 'coach', - 'coachee_response': 200, - 'coachee_nonperm': 404, - 'is_staff': False, - 'workoutid': True, - 'userid': False, - 'dotest': True, - } - ), - ('workouts_view', - { - 'anonymous': False, - 'anonymous_response':302, - 'own': 'basic', - 'own_response': 200, - 'own_nonperm': 302, - 'member': 'basic', - 'member_response': 200, - 'member_nonperm': 404, - 'coachee': 'coach', - 'coachee_response': 200, - 'coachee_nonperm': 404, - 'is_staff': False, - 'workoutid': False, - 'userid': True, - 'dotest': True, - } - ) - ] - - import pandas as pd - df = pd.read_csv('./rowers/tests/viewnames.csv') - - for id, row in df.iterrows(): - view = row['view'] - tpl = (view, - { - 'anonymous':row.anonymous, - 'anonymous_response':row.anonymous_response, - 'own': row.own, - 'own_response':row.own_response, - 'own_nonperm': row.own_nonperm, - 'member': row.member, - 'member_response':row.member_response, - 'member_nonperm': row.member_nonperm, - 'coachee': row.coachee, - 'coachee_response':row.coachee_response, - 'coachee_nonperm': row.coachee_nonperm, - 'is_staff':row.is_staff, - 'workoutid':row.workoutid, - 'userid':row.userid, - 'dotest':row.dotest, - }) - if row.dotest: - viewstotest.append(tpl) - - - plans = ['basic','plan','coach','pro'] - # Test access for anonymous users @parameterized.expand(viewstotest) @patch('rowers.dataprep.create_engine') @@ -249,7 +230,11 @@ class PermissionsViewTests(TestCase): mocked_get_video_data, ): - if permissions['anonymous'] in plans and not permissions['is_staff'] and permissions['dotest']: + print() + print(view,'anonymous',permissions['anonymous'],permissions['is_staff'],permissions['dotest']) + print() + + if permissions['anonymous'] and not permissions['is_staff'] and permissions['dotest']: urlstotest = [] if permissions['workoutid']: url = reverse(view,kwargs={'id':encoder.encode_hex(self.ucoach['workouts'][0].id)}) @@ -258,8 +243,9 @@ class PermissionsViewTests(TestCase): url = reverse(view) urlstotest.append(url) + print(view,'anonymous',urlstotest) + for url in urlstotest: - print(url) result = self.c.get(url) self.assertEqual(result.status_code,permissions['anonymous_response']) @@ -281,6 +267,10 @@ class PermissionsViewTests(TestCase): mocked_get_video_data, ): + print() + print(view,'own') + print() + if permissions['own'] in plans and not permissions['is_staff'] and permissions['dotest']: urlstotest = [] falseurlstotest = [] @@ -290,6 +280,8 @@ class PermissionsViewTests(TestCase): memberuser = self.uplan notuser = None + print(view,'own') + if permissions['own'] == 'basic': thisuser = self.ubasic memberuser = self.uplan @@ -323,23 +315,22 @@ class PermissionsViewTests(TestCase): url = reverse(view) urlstotest.append(url) + print(view,'own',urlstotest,falseurlstotest,otheruserurls) + # test logged in as user who has permissions for url in urlstotest: - print(url) login = self.c.login(username = thisuser['username'],password = thisuser['password']) result = self.c.get(url) self.assertEqual(result.status_code, permissions['own_response']) # test logged as user with no permissions (e.g. too low plan) for url in falseurlstotest: - print(url) login = self.c.login(username = notuser['username'],password = notuser['password']) result = self.c.get(url) self.assertEqual(result.status_code, permissions['own_nonperm']) # test as user with permissions, accessing object of non-related user for url in otheruserurls: - print(url) login = self.c.login(username=thisuser['username'],password = thisuser['password']) result = self.c.get(url) self.assertEqual(result.status_code,403) @@ -362,6 +353,10 @@ class PermissionsViewTests(TestCase): mocked_get_video_data, ): + print() + print(view,'member') + print() + if permissions['member'] in plans and not permissions['is_staff'] and permissions['dotest']: urlstotest = [] falseurlstotest = [] @@ -371,6 +366,8 @@ class PermissionsViewTests(TestCase): memberuser = self.uplan notuser = None + print(view,'member') + if permissions['member'] == 'basic': thisuser = self.ubasic memberuser = self.uplan @@ -405,23 +402,22 @@ class PermissionsViewTests(TestCase): url = reverse(view) urlstotest.append(url) + print('member',urlstotest,falseurlstotest,otheruserurls) + # test logged in as user who has permissions for url in urlstotest: - print(url) login = self.c.login(username = thisuser['username'],password = thisuser['password']) result = self.c.get(url) self.assertEqual(result.status_code, permissions['member_response']) # test logged as user with no permissions (e.g. too low plan) for url in falseurlstotest: - print(url) login = self.c.login(username = notuser['username'],password = notuser['password']) result = self.c.get(url) self.assertEqual(result.status_code, permissions['member_nonperm']) # test as user with permissions, accessing object of non-related user for url in otheruserurls: - print(url) login = self.c.login(username=thisuser['username'],password = thisuser['password']) result = self.c.get(url) self.assertEqual(result.status_code,403) @@ -444,6 +440,10 @@ class PermissionsViewTests(TestCase): mocked_get_video_data, ): + print() + print(view,'coachee') + print() + if permissions['coachee'] in plans and not permissions['is_staff'] and permissions['dotest']: urlstotest = [] falseurlstotest = [] @@ -470,16 +470,16 @@ class PermissionsViewTests(TestCase): url = reverse(view) urlstotest.append(url) + print('coachee',urlstotest,falseurlstotest,otheruserurls) + # test logged in as user who has permissions for url in urlstotest: - print(url) login = self.c.login(username = thisuser['username'],password = thisuser['password']) result = self.c.get(url) self.assertEqual(result.status_code, permissions['coachee_response']) # test as user with permissions, accessing object of non-related user for url in otheruserurls: - print(url) login = self.c.login(username=thisuser['username'],password = thisuser['password']) result = self.c.get(url) self.assertEqual(result.status_code,403) diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 5889fe8e..23abc42c 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -1,312 +1,312 @@ ,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest 0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,404,404,FALSE,404,404,FALSE,FALSE,FALSE,TRUE -1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,TRUE,FALSE,FALSE,TRUE -2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -4,5,fitness_metric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -5,6,agegrouprecordview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -6,7,workouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -7,8,virtualevents_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -8,9,virtualevent_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -9,10,indoorvirtualevent_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -10,11,virtualevent_toggle_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -11,12,indoorvirtualevent_toggle_email_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -12,13,virtualevent_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -13,14,virtualevent_ranking_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -14,15,virtualevent_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -15,16,indoorvirtualevent_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -16,17,virtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -17,18,indoorvirtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -18,19,virtualevent_addboat_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -19,20,virtualevent_withdraw_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -20,21,virtualevent_submit_result_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -21,22,virtualevent_disqualify_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -22,24,courses_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -23,25,course_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -24,26,addmanual_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -25,27,team_comparison_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,TRUE,FALSE -26,28,workouts_join_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -27,29,workouts_join_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -28,30,user_boxplot_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -29,31,analysis_new,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -30,32,user_multiflex_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -31,33,session_jobs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -32,34,session_jobs_status,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -33,35,kill_async_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -34,36,post_progress,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -35,37,graphs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -36,38,fitnessmetric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -37,39,rankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -38,40,rankings_view2,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -39,41,otwrankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -40,42,oterankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -41,43,cum_flex,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -42,44,analysis_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -43,47,cum_flex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -44,48,histo,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -45,49,histo_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -46,51,cumstats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -47,53,graph_show_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -48,54,GraphDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -49,55,get_thumbnails,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -50,56,otw_use_impeller,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -51,57,otw_use_gps,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -52,58,workout_toggle_ranking,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -53,59,team_workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -54,60,workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -55,61,workout_histo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -56,62,workout_forcecurve_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -57,63,workout_unsubscribe_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -58,64,workout_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -59,65,workout_tcxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -60,66,workout_gpxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -61,67,workout_csvemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -62,68,workout_csvtoadmin_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -63,69,workout_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -64,70,workout_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -65,71,workout_update_cp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -66,72,instroke_chart,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -67,73,instroke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -68,74,workout_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -69,75,workout_data_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -70,76,workout_otwsetpower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -71,77,workout_otwpowerplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -72,78,workout_wind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -73,79,workout_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -74,80,virtualevent_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -75,81,virtualevent_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -76,82,virtualevent_setlogo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -77,83,logo_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -78,84,workout_downloadwind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -79,85,workout_downloadmetar_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -80,86,workout_stream_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -81,87,workout_summary_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -82,88,workout_summary_restore_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -83,89,workout_split_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -84,90,workout_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -85,91,workout_video_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -86,92,VideoDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -87,93,workout_video_view_mini,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -88,94,workout_video_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -89,95,list_videos,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -90,96,video_selectworkout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -91,98,workout_fusion_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -92,99,workout_fusion_list,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -93,100,TemplateView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -94,103,workout_add_chart_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -95,112,WorkoutDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -96,113,workout_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -97,114,workout_undo_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -98,115,workout_c2import_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -99,120,workout_stravaimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -100,122,workout_getc2workout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -101,124,workout_getimportview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -102,125,workout_getstravaworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -103,126,workout_getstravaworkout_next,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -104,127,workout_sporttracksimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -105,129,workout_getsporttracksworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -106,130,workout_polarimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -107,132,workout_runkeeperimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -108,134,workout_underarmourimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -109,135,workout_c2_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -110,136,workout_strava_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -111,137,workout_recalcsummary_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -112,138,workout_sporttracks_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -113,139,workout_runkeeper_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -114,140,workout_underarmour_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -115,141,workout_tp_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -116,142,multi_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -119,148,alert_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -120,150,alert_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -121,152,alert_report_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -122,155,boxplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -123,157,boxplot_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -124,158,multiflex_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -125,160,multiflex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -126,161,deactivate_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -127,162,remove_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -128,163,user_gdpr_confirm,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -129,165,user_gdpr_optin,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -130,167,rower_teams_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -131,168,rower_calcdps_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -132,169,rower_exportsettings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -133,171,team_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -134,172,team_members_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -135,173,team_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -136,174,team_leaveconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -137,175,team_leave_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -138,176,team_deleteconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -139,177,team_requestmembership_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -140,178,reject_revoke_coach_request,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -141,179,coach_drop_athlete_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -142,180,coach_drop_athlete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -143,181,athlete_drop_coach_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -144,182,athlete_drop_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -145,184,reject_revoke_coach_offer,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -146,186,request_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -147,187,offer_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -148,188,coach_accept_coachrequest_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -149,189,rower_accept_coachoffer_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -150,190,team_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -151,191,team_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -152,192,manager_member_drop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -153,193,invitation_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -154,194,invitation_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -155,195,rower_invitations_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -156,196,raise_500,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -157,198,request_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -158,199,request_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -159,200,manager_requests_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -160,202,rower_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -161,204,rower_prefs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,TRUE,FALSE,FALSE -240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE -307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE -310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,TRUE,FALSE +1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +4,5,fitness_metric_view,updates a person's fitness metric,TRUE,302,basic,302,302,FALSE,404,302,FALSE,404,404,FALSE,FALSE,FALSE,TRUE +5,6,agegrouprecordview,shows ergo age group records,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,404,coach,200,404,FALSE,TRUE,FALSE,TRUE +7,8,virtualevents_view,virtual races list,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +8,9,virtualevent_create_view,create virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +9,10,indoorvirtualevent_create_view,create indoor virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +10,11,virtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +11,12,indoorvirtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +12,13,virtualevent_view,view virtual race,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +13,14,virtualevent_ranking_view,view ranking,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +14,15,virtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +15,16,indoorvirtualevent_edit_view,edit race,TRUE,303,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +16,17,virtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +17,18,indoorvirtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +18,19,virtualevent_addboat_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +19,20,virtualevent_withdraw_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +20,21,virtualevent_submit_result_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +21,22,virtualevent_disqualify_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +22,24,courses_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +23,25,course_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +24,26,addmanual_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +25,27,team_comparison_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,TRUE,FALSE +26,28,workouts_join_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +27,29,workouts_join_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +28,30,user_boxplot_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +29,31,analysis_new,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +30,32,user_multiflex_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +31,33,session_jobs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +32,34,session_jobs_status,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +33,35,kill_async_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +34,36,post_progress,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +35,37,graphs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +36,38,fitnessmetric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +37,39,rankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +38,40,rankings_view2,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +39,41,otwrankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +40,42,oterankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +41,43,cum_flex,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +42,44,analysis_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +43,47,cum_flex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +44,48,histo,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +45,49,histo_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +46,51,cumstats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +47,53,graph_show_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +48,54,GraphDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +49,55,get_thumbnails,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +50,56,otw_use_impeller,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +51,57,otw_use_gps,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +52,58,workout_toggle_ranking,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +53,59,team_workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +54,60,workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +55,61,workout_histo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +56,62,workout_forcecurve_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +57,63,workout_unsubscribe_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +58,64,workout_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +59,65,workout_tcxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +60,66,workout_gpxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +61,67,workout_csvemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +62,68,workout_csvtoadmin_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +63,69,workout_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +64,70,workout_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +65,71,workout_update_cp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +66,72,instroke_chart,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +67,73,instroke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +68,74,workout_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +69,75,workout_data_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +70,76,workout_otwsetpower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +71,77,workout_otwpowerplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +72,78,workout_wind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +73,79,workout_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +74,80,virtualevent_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +75,81,virtualevent_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +76,82,virtualevent_setlogo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +77,83,logo_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +78,84,workout_downloadwind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +79,85,workout_downloadmetar_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +80,86,workout_stream_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +81,87,workout_summary_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +82,88,workout_summary_restore_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +83,89,workout_split_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +84,90,workout_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +85,91,workout_video_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +86,92,VideoDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +87,93,workout_video_view_mini,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +88,94,workout_video_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +89,95,list_videos,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +90,96,video_selectworkout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +91,98,workout_fusion_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +92,99,workout_fusion_list,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +93,100,TemplateView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +94,103,workout_add_chart_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +95,112,WorkoutDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +96,113,workout_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +97,114,workout_undo_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +98,115,workout_c2import_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +99,120,workout_stravaimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +100,122,workout_getc2workout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +101,124,workout_getimportview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +102,125,workout_getstravaworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +103,126,workout_getstravaworkout_next,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +104,127,workout_sporttracksimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +105,129,workout_getsporttracksworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +106,130,workout_polarimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +107,132,workout_runkeeperimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +108,134,workout_underarmourimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +109,135,workout_c2_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +110,136,workout_strava_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +111,137,workout_recalcsummary_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +112,138,workout_sporttracks_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +113,139,workout_runkeeper_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +114,140,workout_underarmour_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +115,141,workout_tp_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +116,142,multi_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +119,148,alert_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +120,150,alert_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +121,152,alert_report_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +122,155,boxplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +123,157,boxplot_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +124,158,multiflex_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +125,160,multiflex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +126,161,deactivate_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +127,162,remove_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +128,163,user_gdpr_confirm,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +129,165,user_gdpr_optin,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +130,167,rower_teams_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +131,168,rower_calcdps_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +132,169,rower_exportsettings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +133,171,team_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +134,172,team_members_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +135,173,team_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +136,174,team_leaveconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +137,175,team_leave_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +138,176,team_deleteconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +139,177,team_requestmembership_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +140,178,reject_revoke_coach_request,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +141,179,coach_drop_athlete_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +142,180,coach_drop_athlete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +143,181,athlete_drop_coach_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +144,182,athlete_drop_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +145,184,reject_revoke_coach_offer,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +146,186,request_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +147,187,offer_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +148,188,coach_accept_coachrequest_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +149,189,rower_accept_coachoffer_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +150,190,team_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +151,191,team_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +152,192,manager_member_drop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +153,193,invitation_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +154,194,invitation_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +155,195,rower_invitations_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +156,196,raise_500,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +157,198,request_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +158,199,request_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +159,200,manager_requests_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +160,202,rower_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +161,204,rower_prefs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE diff --git a/rowers/urls.py b/rowers/urls.py index 91614718..7ef0a425 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -153,6 +153,8 @@ urlpatterns = [ views.agegrouprecordview,name='agegrouprecordview'), re_path(r'^agegrouprecords/(?P\w+.*)/(?P\w+.*)/$', views.agegrouprecordview,name='agegrouprecordview'), + re_path(r'^agegrouprecords/$', + views.agegrouprecordview,name='agegrouprecordview'), re_path(r'^list-workouts/ranking/$',views.workouts_view,{'rankingonly':True}, name='workouts_view'), # re_path(r'^list-workouts/team/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.workouts_view, From 07acd1f192a09c424d6bef5521bc95f50963492c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 22 Jan 2020 22:29:45 +0100 Subject: [PATCH 52/80] passing a few more tests, fixed some permissions bugs --- rowers/decorators.py | 4 +++ rowers/rower_rules.py | 16 +++++++++++ rowers/tests/.~lock.viewnames.csv# | 1 + rowers/tests/test_permissions2.py | 2 +- rowers/tests/viewnames.csv | 44 +++++++++++++++--------------- rowers/urls.py | 1 + rowers/views/racesviews.py | 1 - rowers/views/statements.py | 3 +- rowers/views/workoutviews.py | 17 ++++++++---- 9 files changed, 58 insertions(+), 31 deletions(-) create mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/decorators.py b/rowers/decorators.py index 4bd1c7a3..78dc7af6 100644 --- a/rowers/decorators.py +++ b/rowers/decorators.py @@ -4,6 +4,7 @@ from __future__ import print_function from __future__ import unicode_literals from django.contrib.auth.decorators import login_required,user_passes_test +from django.urls import reverse from django.http import HttpResponseRedirect from django.core.exceptions import PermissionDenied @@ -29,6 +30,9 @@ def user_passes_test(test_func, message=default_message,login_url=None,redirect_ def decorator(view_func): @wraps(view_func, assigned=available_attrs(view_func)) def _wrapped_view(request, *args, **kwargs): + if request.user.is_anonymous: + url = reverse('login')+'?next='+request.path + return HttpResponseRedirect(url) if not test_func(request.user): messages.error(request, message) return HttpResponseRedirect(login_url) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index dd78d76b..93bbdcd7 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -319,6 +319,22 @@ def is_workout_user(user,workout): return is_coach_user(user,workout.user.user) +# check if user is in same team as owner of workout +@rules.predicate +def is_workout_team(user,workout): + if user.is_anonymous: + return False + + try: + r = user.rower + except AttributeError: + return False + + if workout.user == r: + return True + + return is_rower_team_member(user,workout.user) + # check if user can see workout @rules.predicate def can_view_workout(user,workout): diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# new file mode 100644 index 00000000..9775fe7e --- /dev/null +++ b/rowers/tests/.~lock.viewnames.csv# @@ -0,0 +1 @@ +,sander,sander-pc,22.01.2020 22:27,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index bc2e6b36..f9ebb810 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -393,7 +393,7 @@ class PermissionsViewTests(TestCase): if notuser: falseurlstotest.append(url) elif permissions['userid']: - url = reverse(view,kwargs={'userid':thisuser['user'].id}) + url = reverse(view,kwargs={'userid':memberuser['user'].id}) urlstotest.append(url) url = reverse(view,kwargs={'userid':self.ustrange['user'].id}) diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 23abc42c..12dca557 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -1,33 +1,33 @@ ,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest -0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,404,404,FALSE,404,404,FALSE,FALSE,FALSE,TRUE +0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,TRUE 1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE 2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE 3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -4,5,fitness_metric_view,updates a person's fitness metric,TRUE,302,basic,302,302,FALSE,404,302,FALSE,404,404,FALSE,FALSE,FALSE,TRUE +4,5,fitness_metric_view,updates a person's fitness metric,TRUE,302,basic,302,302,FALSE,403,302,FALSE,403,403,FALSE,FALSE,FALSE,TRUE 5,6,agegrouprecordview,shows ergo age group records,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,404,coach,200,404,FALSE,TRUE,FALSE,TRUE +6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,403,coach,200,403,FALSE,TRUE,FALSE,TRUE 7,8,virtualevents_view,virtual races list,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE 8,9,virtualevent_create_view,create virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE 9,10,indoorvirtualevent_create_view,create indoor virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -10,11,virtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -11,12,indoorvirtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -12,13,virtualevent_view,view virtual race,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -13,14,virtualevent_ranking_view,view ranking,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -14,15,virtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -15,16,indoorvirtualevent_edit_view,edit race,TRUE,303,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -16,17,virtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -17,18,indoorvirtualevent_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -18,19,virtualevent_addboat_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -19,20,virtualevent_withdraw_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -20,21,virtualevent_submit_result_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -21,22,virtualevent_disqualify_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -22,24,courses_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -23,25,course_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -24,26,addmanual_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -25,27,team_comparison_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,TRUE,FALSE -26,28,workouts_join_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -27,29,workouts_join_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -28,30,user_boxplot_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +10,11,virtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +11,12,indoorvirtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +12,13,virtualevent_view,view virtual race,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +13,14,virtualevent_ranking_view,view ranking,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +14,15,virtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +15,16,indoorvirtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +16,17,virtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +17,18,indoorvirtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +18,19,virtualevent_addboat_view,add a new boat type,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +19,20,virtualevent_withdraw_view,withdraw,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +20,21,virtualevent_submit_result_view,submit a result,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,TRUE,FALSE +21,22,virtualevent_disqualify_view,disqualify a racer,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE +22,24,courses_view,see race courses,TRUE,200,FALSE,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +23,25,course_upload_view,upload a new course,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +24,26,addmanual_view,upload a manual workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE +25,27,team_comparison_select,compare with a team member workout,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,TRUE,TRUE,TRUE +26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE +27,29,workouts_join_select,select workouts to join,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE +28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,TRUE 29,31,analysis_new,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE 30,32,user_multiflex_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE 31,33,session_jobs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE diff --git a/rowers/urls.py b/rowers/urls.py index 7ef0a425..dbae602e 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -220,6 +220,7 @@ urlpatterns = [ # re_path(r'^workouts-join-select/team/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.workouts_join_select, # name='workouts_join_select'), re_path(r'^workouts-join/$',views.workouts_join_view,name='workouts_join_view'), + re_path(r'^workouts-join/user/(?P\d+)$',views.workouts_join_view,name='workouts_join_view'), re_path(r'^workouts-join-select/team/(?P\d+)/$',views.workouts_join_select,name='workouts_join_select'), # re_path(r'^workouts-join-select/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.workouts_join_select,name='workouts_join_select'), re_path(r'^workouts-join-select/$',views.workouts_join_select,name='workouts_join_select'), diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py index 358afddd..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) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index e6b43cb3..19deb0cf 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -45,7 +45,7 @@ from rowers.rower_rules import ( 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 + can_start_trial, can_start_plantrial,can_plan,is_workout_team ) from django.shortcuts import render @@ -389,6 +389,7 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): r = getrower(request.user) u = r.user + except Rower.DoesNotExist: raise Http404("Rower doesn't exist") diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index c4ac32c5..376f563e 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -797,8 +797,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) @@ -1039,6 +1042,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) @@ -1047,16 +1051,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: @@ -1211,6 +1214,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: @@ -1338,7 +1342,8 @@ def team_comparison_select(request, if id: firstworkout = get_workout(id) - if not is_workout_user(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)) From 31a7b9fffc2576f12f386f78f2b036b5a678f434 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 22 Jan 2020 22:33:07 +0100 Subject: [PATCH 53/80] fix --- rowers/tests/.~lock.viewnames.csv# | 1 - rowers/tests/viewnames.csv | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# deleted file mode 100644 index 9775fe7e..00000000 --- a/rowers/tests/.~lock.viewnames.csv# +++ /dev/null @@ -1 +0,0 @@ -,sander,sander-pc,22.01.2020 22:27,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 12dca557..4b609ebd 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -26,7 +26,7 @@ 24,26,addmanual_view,upload a manual workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE 25,27,team_comparison_select,compare with a team member workout,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,TRUE,TRUE,TRUE 26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE -27,29,workouts_join_select,select workouts to join,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE +27,29,workouts_join_select,select workouts to join,TRUE,404,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE 28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,TRUE 29,31,analysis_new,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE 30,32,user_multiflex_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE From 466383410d0de4676bcb5b3cefd89467c0c304df Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 25 Jan 2020 15:59:48 +0100 Subject: [PATCH 54/80] a few more tests --- 23099b58-7632-4d34-97e2-2f4890cf5773.tcx | 2523 ++++++++++++++++++++++ rowers/tests/.~lock.viewnames.csv# | 1 + rowers/tests/viewnames.csv | 24 +- rowers/urls.py | 9 +- rowers/views/analysisviews.py | 190 +- rowers/views/workoutviews.py | 61 +- 6 files changed, 2650 insertions(+), 158 deletions(-) create mode 100644 23099b58-7632-4d34-97e2-2f4890cf5773.tcx create mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/23099b58-7632-4d34-97e2-2f4890cf5773.tcx b/23099b58-7632-4d34-97e2-2f4890cf5773.tcx new file mode 100644 index 00000000..e7df305a --- /dev/null +++ b/23099b58-7632-4d34-97e2-2f4890cf5773.tcx @@ -0,0 +1,2523 @@ + + + + + 2016-05-20T13:41:26.962390+00:00 + + 537 + 2000 + 118 + + 148 + + + 156 + + Active + 21 + Manual + + + + 5.4 + + 127 + + 0 + + + 19 + + + + + + 13.1 + + 127 + + 19 + + + 26 + + + + + + 21.0 + + 128 + + 20 + + + 45 + + + + + + 30.3 + + 129 + + 20 + + + 64 + + + + + + 39.0 + + 130 + + 20 + + + 74 + + + + + + 48.2 + + 131 + + 21 + + + 80 + + + + + + 57.6 + + 131 + + 20 + + + 83 + + + + + + 66.4 + + 132 + + 20 + + + 87 + + + + + + 75.5 + + 132 + + 21 + + + 86 + + + + + + 85.1 + + 132 + + 20 + + + 88 + + + + + + 95.0 + + 132 + + 21 + + + 100 + + + + + + 105.0 + + 133 + + 22 + + + 127 + + + + + + 115.3 + + 134 + + 21 + + + 135 + + + + + + 125.8 + + 135 + + 21 + + + 139 + + + + + + 136.6 + + 136 + + 21 + + + 146 + + + + + + 147.2 + + 137 + + 22 + + + 150 + + + + + + 157.6 + + 139 + + 22 + + + 152 + + + + + + 167.8 + + 140 + + 21 + + + 146 + + + + + + 178.5 + + 140 + + 22 + + + 150 + + + + + + 188.5 + + 141 + + 21 + + + 155 + + + + + + 199.3 + + 141 + + 21 + + + 148 + + + + + + 209.4 + + 142 + + 22 + + + 151 + + + + + + 219.4 + + 142 + + 22 + + + 151 + + + + + + 230.2 + + 143 + + 22 + + + 148 + + + + + + 240.2 + + 144 + + 22 + + + 147 + + + + + + 250.1 + + 145 + + 23 + + + 150 + + + + + + 259.6 + + 145 + + 23 + + + 152 + + + + + + 270.3 + + 145 + + 23 + + + 152 + + + + + + 280.6 + + 145 + + 22 + + + 149 + + + + + + 290.7 + + 144 + + 22 + + + 150 + + + + + + 300.8 + + 145 + + 23 + + + 149 + + + + + + 311.1 + + 145 + + 22 + + + 152 + + + + + + 321.2 + + 145 + + 22 + + + 157 + + + + + + 331.9 + + 145 + + 21 + + + 150 + + + + + + 342.0 + + 146 + + 22 + + + 151 + + + + + + 352.4 + + 146 + + 22 + + + 151 + + + + + + 363.0 + + 146 + + 22 + + + 153 + + + + + + 373.4 + + 147 + + 22 + + + 152 + + + + + + 383.9 + + 147 + + 22 + + + 153 + + + + + + 394.6 + + 147 + + 22 + + + 152 + + + + + + 405.0 + + 147 + + 21 + + + 149 + + + + + + 415.3 + + 148 + + 22 + + + 152 + + + + + + 426.0 + + 148 + + 22 + + + 151 + + + + + + 436.5 + + 148 + + 21 + + + 149 + + + + + + 446.9 + + 148 + + 22 + + + 149 + + + + + + 456.9 + + 149 + + 22 + + + 156 + + + + + + 467.6 + + 149 + + 22 + + + 155 + + + + + + 478.5 + + 150 + + 22 + + + 156 + + + + + + 489.0 + + 150 + + 21 + + + 154 + + + + + + 499.1 + + 150 + + 21 + + + 148 + + + + + + 510.0 + + 150 + + 22 + + + 151 + + + + + + 519.9 + + 149 + + 22 + + + 153 + + + + + + 530.6 + + 149 + + 22 + + + 151 + + + + + + 540.8 + + 149 + + 22 + + + 148 + + + + + + 550.8 + + 148 + + 22 + + + 149 + + + + + + 560.8 + + 148 + + 22 + + + 144 + + + + + + 571.0 + + 147 + + 22 + + + 149 + + + + + + 580.7 + + 147 + + 22 + + + 150 + + + + + + 591.2 + + 147 + + 22 + + + 151 + + + + + + 601.4 + + 147 + + 22 + + + 150 + + + + + + 611.4 + + 147 + + 23 + + + 153 + + + + + + 621.8 + + 147 + + 23 + + + 151 + + + + + + 632.1 + + 147 + + 22 + + + 155 + + + + + + 642.3 + + 147 + + 22 + + + 154 + + + + + + 652.0 + + 148 + + 23 + + + 157 + + + + + + 662.7 + + 148 + + 23 + + + 162 + + + + + + 673.1 + + 148 + + 23 + + + 163 + + + + + + 683.6 + + 149 + + 22 + + + 163 + + + + + + 693.8 + + 149 + + 22 + + + 162 + + + + + + 703.8 + + 149 + + 22 + + + 164 + + + + + + 714.7 + + 150 + + 23 + + + 162 + + + + + + 724.9 + + 150 + + 22 + + + 162 + + + + + + 735.2 + + 151 + + 23 + + + 159 + + + + + + 745.4 + + 151 + + 22 + + + 158 + + + + + + 756.0 + + 151 + + 23 + + + 164 + + + + + + 766.3 + + 150 + + 22 + + + 163 + + + + + + 776.5 + + 150 + + 22 + + + 161 + + + + + + 786.9 + + 150 + + 23 + + + 163 + + + + + + 797.2 + + 150 + + 22 + + + 165 + + + + + + 807.8 + + 150 + + 23 + + + 166 + + + + + + 818.2 + + 150 + + 23 + + + 166 + + + + + + 828.4 + + 150 + + 22 + + + 168 + + + + + + 839.2 + + 150 + + 23 + + + 169 + + + + + + 849.6 + + 151 + + 23 + + + 166 + + + + + + 860.1 + + 151 + + 22 + + + 172 + + + + + + 870.3 + + 152 + + 22 + + + 172 + + + + + + 881.1 + + 152 + + 22 + + + 169 + + + + + + 891.7 + + 152 + + 23 + + + 167 + + + + + + 902.1 + + 152 + + 22 + + + 164 + + + + + + 913.1 + + 152 + + 22 + + + 161 + + + + + + 923.9 + + 153 + + 22 + + + 158 + + + + + + 934.6 + + 154 + + 21 + + + 158 + + + + + + 945.4 + + 154 + + 21 + + + 154 + + + + + + 956.0 + + 155 + + 21 + + + 155 + + + + + + 966.7 + + 155 + + 21 + + + 152 + + + + + + 977.4 + + 156 + + 21 + + + 150 + + + + + + 988.1 + + 156 + + 21 + + + 157 + + + + + + 998.8 + + 156 + + 21 + + + 155 + + + + + + 1009.6 + + 156 + + 21 + + + 151 + + + + + + 1020.6 + + 156 + + 21 + + + 147 + + + + + + 1031.5 + + 156 + + 20 + + + 145 + + + + + + 1042.5 + + 156 + + 21 + + + 144 + + + + + + 1053.3 + + 155 + + 20 + + + 145 + + + + + + 1064.1 + + 155 + + 21 + + + 147 + + + + + + 1075.3 + + 155 + + 20 + + + 142 + + + + + + 1086.1 + + 155 + + 20 + + + 136 + + + + + + 1097.5 + + 155 + + 21 + + + 141 + + + + + + 1108.5 + + 155 + + 20 + + + 146 + + + + + + 1119.2 + + 155 + + 20 + + + 143 + + + + + + 1130.6 + + 155 + + 20 + + + 143 + + + + + + 1141.3 + + 155 + + 20 + + + 143 + + + + + + 1152.4 + + 155 + + 21 + + + 142 + + + + + + 1163.3 + + 155 + + 20 + + + 138 + + + + + + 1173.8 + + 154 + + 20 + + + 141 + + + + + + 1184.8 + + 154 + + 21 + + + 146 + + + + + + 1195.8 + + 153 + + 21 + + + 146 + + + + + + 1206.6 + + 152 + + 21 + + + 141 + + + + + + 1217.3 + + 153 + + 21 + + + 141 + + + + + + 1227.8 + + 152 + + 21 + + + 140 + + + + + + 1238.7 + + 152 + + 21 + + + 143 + + + + + + 1249.5 + + 151 + + 21 + + + 149 + + + + + + 1260.1 + + 151 + + 20 + + + 141 + + + + + + 1270.9 + + 151 + + 21 + + + 141 + + + + + + 1281.8 + + 150 + + 21 + + + 145 + + + + + + 1292.7 + + 151 + + 20 + + + 142 + + + + + + 1303.4 + + 151 + + 20 + + + 141 + + + + + + 1314.3 + + 151 + + 21 + + + 141 + + + + + + 1325.2 + + 151 + + 21 + + + 146 + + + + + + 1336.1 + + 152 + + 20 + + + 143 + + + + + + 1346.9 + + 152 + + 21 + + + 144 + + + + + + 1357.4 + + 152 + + 20 + + + 141 + + + + + + 1368.1 + + 152 + + 21 + + + 138 + + + + + + 1379.0 + + 152 + + 20 + + + 142 + + + + + + 1389.5 + + 153 + + 21 + + + 145 + + + + + + 1399.9 + + 152 + + 21 + + + 138 + + + + + + 1410.7 + + 152 + + 20 + + + 139 + + + + + + 1422.0 + + 152 + + 20 + + + 139 + + + + + + 1432.8 + + 151 + + 20 + + + 141 + + + + + + 1443.6 + + 151 + + 21 + + + 146 + + + + + + 1454.4 + + 152 + + 20 + + + 143 + + + + + + 1465.1 + + 151 + + 21 + + + 143 + + + + + + 1475.9 + + 152 + + 21 + + + 145 + + + + + + 1486.6 + + 152 + + 21 + + + 148 + + + + + + 1497.4 + + 153 + + 21 + + + 143 + + + + + + 1508.2 + + 153 + + 20 + + + 140 + + + + + + 1519.2 + + 154 + + 20 + + + 144 + + + + + + 1530.0 + + 154 + + 21 + + + 143 + + + + + + 1540.9 + + 153 + + 20 + + + 141 + + + + + + 1551.3 + + 153 + + 21 + + + 143 + + + + + + 1562.6 + + 153 + + 21 + + + 146 + + + + + + 1573.3 + + 153 + + 20 + + + 141 + + + + + + 1584.2 + + 152 + + 20 + + + 139 + + + + + + 1594.6 + + 152 + + 21 + + + 145 + + + + + + 1606.0 + + 152 + + 21 + + + 143 + + + + + + 1616.2 + + 152 + + 20 + + + 138 + + + + + + 1627.4 + + 152 + + 21 + + + 140 + + + + + + 1638.0 + + 152 + + 21 + + + 144 + + + + + + 1649.2 + + 151 + + 20 + + + 143 + + + + + + 1660.2 + + 152 + + 20 + + + 143 + + + + + + 1670.8 + + 151 + + 20 + + + 142 + + + + + + 1681.4 + + 151 + + 21 + + + 140 + + + + + + 1692.1 + + 151 + + 21 + + + 140 + + + + + + 1702.5 + + 150 + + 21 + + + 141 + + + + + + 1713.7 + + 150 + + 21 + + + 144 + + + + + + 1724.4 + + 150 + + 21 + + + 146 + + + + + + 1735.1 + + 150 + + 20 + + + 141 + + + + + + 1745.6 + + 150 + + 21 + + + 140 + + + + + + 1756.3 + + 150 + + 21 + + + 141 + + + + + + 1766.2 + + 151 + + 20 + + + 142 + + + + + + 1777.1 + + 150 + + 22 + + + 138 + + + + + + 1787.5 + + 150 + + 21 + + + 138 + + + + + + 1797.7 + + 150 + + 22 + + + 140 + + + + + + 1808.4 + + 150 + + 21 + + + 140 + + + + + + 1818.4 + + 149 + + 21 + + + 138 + + + + + + 1828.9 + + 149 + + 22 + + + 146 + + + + + + 1839.9 + + 149 + + 21 + + + 142 + + + + + + 1850.5 + + 148 + + 21 + + + 142 + + + + + + 1861.2 + + 148 + + 21 + + + 145 + + + + + + 1871.9 + + 147 + + 21 + + + 143 + + + + + + 1882.6 + + 147 + + 20 + + + 139 + + + + + + 1893.3 + + 148 + + 20 + + + 140 + + + + + + 1904.3 + + 149 + + 21 + + + 144 + + + + + + 1915.4 + + 149 + + 20 + + + 148 + + + + + + 1926.2 + + 150 + + 20 + + + 139 + + + + + + 1937.3 + + 151 + + 20 + + + 140 + + + + + + 1947.8 + + 152 + + 20 + + + 144 + + + + + + 1959.1 + + 152 + + 20 + + + 142 + + + + + + 1969.8 + + 153 + + 20 + + + 140 + + + + + + 1980.6 + + 153 + + 21 + + + 143 + + + + + + 1991.4 + + 153 + + 21 + + + 143 + + + + + + 2000.0 + + 154 + + 21 + + + 147 + + + + + + <Element 'Notes' at 0x7f433c451b38> + + + + rowsandall.com/rowingdata + + + rowingdata + + + 0 + 75 + + Release + + EN + 000-00000-00 + + diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# new file mode 100644 index 00000000..6dee4878 --- /dev/null +++ b/rowers/tests/.~lock.viewnames.csv# @@ -0,0 +1 @@ +,sander,sander-pc,25.01.2020 15:42,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 4b609ebd..1611ffa6 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -28,18 +28,18 @@ 26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE 27,29,workouts_join_select,select workouts to join,TRUE,404,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE 28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,TRUE -29,31,analysis_new,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -30,32,user_multiflex_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -31,33,session_jobs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -32,34,session_jobs_status,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -33,35,kill_async_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -34,36,post_progress,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -35,37,graphs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -36,38,fitnessmetric_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -37,39,rankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -38,40,rankings_view2,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -39,41,otwrankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -40,42,oterankings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +29,31,analysis_new,analysis front page,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE +30,32,user_multiflex_select,select multiflex data,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE +31,33,session_jobs_view,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE +32,34,session_jobs_status,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE +33,35,kill_async_job,kill job,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +34,36,post_progress,post progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +35,37,graphs_view,view charts,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE +36,38,fitnessmetric_view,view fitness metric,TRUE,302,plan,200,302,plan,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE +37,39,rankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE +38,40,rankings_view2,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE +39,41,otwrankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE +40,42,oterankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE 41,43,cum_flex,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE 42,44,analysis_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE 43,47,cum_flex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE diff --git a/rowers/urls.py b/rowers/urls.py index dbae602e..78072d0d 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -246,6 +246,7 @@ urlpatterns = [ re_path(r'^record-progress/(?P.*)/$',views.post_progress), re_path(r'^record-progress/$',views.post_progress), re_path(r'^list-graphs/$',views.graphs_view,name='graphs_view'), + re_path(r'^list-graphs/user/(?P\d+)/$',views.graphs_view,name='graphs_view'), re_path(r'^fitness-progress/$',views.fitnessmetric_view,name='fitnessmetric_view'), re_path(r'^fitness-progress/user/(?P\d+)/$',views.fitnessmetric_view,name='fitnessmetric_view'), re_path(r'^fitness-progress/user/(?P\d+)/(?P\w+.*)/$',views.fitnessmetric_view,name='fitnessmetric_view'), @@ -255,17 +256,17 @@ urlpatterns = [ re_path(r'^ote-bests/$',views.rankings_view,name='rankings_view'), re_path(r'^(?P\d+)/ote-bests/$',views.rankings_view,name='rankings_view'), # re_path(r'^(?P\d+)/ote-bests2/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.rankings_view2,name='rankings_view2'), - re_path(r'^ote-bests2/user/(?P\d+)/$',views.rankings_view2,name='rankings_view2'), + re_path(r'^ote-bests2/user/(?P\d+)/$',views.rankings_view2,name='rankings_view2'), # re_path(r'^ote-bests2/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.rankings_view2,name='rankings_view2'), re_path(r'^ote-bests2/$',views.rankings_view2,name='rankings_view2'), # re_path(r'^otw-bests/user/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.otwrankings_view,name='otwrankings_view'), # re_path(r'^otw-bests/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.otwrankings_view,name='otwrankings_view'), - re_path(r'^otw-bests/user/(?P\d+)/$',views.otwrankings_view,name='otwrankings_view'), + re_path(r'^otw-bests/user/(?P\d+)/$',views.otwrankings_view,name='otwrankings_view'), re_path(r'^otw-bests/$',views.otwrankings_view,name='otwrankings_view'), # re_path(r'^ote-ranking/user/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.oterankings_view,name='oterankings_view'), # re_path(r'^ote-ranking/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.oterankings_view,name='oterankings_view'), re_path(r'^ote-ranking/$',views.oterankings_view,name='oterankings_view'), - re_path(r'^ote-ranking/user/(?P\d+)/$',views.oterankings_view,name='oterankings_view'), + re_path(r'^ote-ranking/user/(?P\d+)/$',views.oterankings_view,name='oterankings_view'), # re_path(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/user/(?P\d+)/$',views.cum_flex,name='cum_flex'), # re_path(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.cum_flex,name='cum_flex'), re_path(r'^flexall/(?P\w+.*)/(?P\w+.*)/(?P\w+.*)/$',views.cum_flex,name='cum_flex'), @@ -450,7 +451,7 @@ urlpatterns = [ re_path(r'^user-multiflex-data/$',views.multiflex_data,name='multiflex_data'), re_path(r'^me/deactivate/$',views.deactivate_user,name='deactivate_user'), re_path(r'^me/delete/$',views.remove_user,name='remove_user'), -# re_path(r'^survey/$',views.survey,name='survey'), + re_path(r'^survey/$',views.survey,name='survey'), re_path(r'^me/gdpr-optin-confirm/?/$',views.user_gdpr_confirm,name='user_gdpr_confirm'), re_path(r'^me/gdpr-optin-confirm/$',views.user_gdpr_confirm,name='user_gdpr_confirm'), re_path(r'^me/gdpr-optin/?/$',views.user_gdpr_optin,name='user_gdpr_optin'), diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index be1190da..b8a79ad3 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -1741,7 +1741,7 @@ def ajax_agegrouprecords(request, # Show ranking distances including predicted paces @login_required() -def rankings_view2(request,theuser=0, +def rankings_view2(request,userid=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), deltadays=-1, @@ -1762,83 +1762,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 @@ -1887,10 +1888,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 @@ -2155,7 +2153,7 @@ def rankings_view2(request,theuser=0, 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 otwrankings_view(request,theuser=0, +def otwrankings_view(request,userid=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), startdatestring="", @@ -2178,22 +2176,9 @@ 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=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 @@ -2247,10 +2232,7 @@ def otwrankings_view(request,theuser=0, - try: - uu = User.objects.get(id=theuser) - except User.DoesNotExist: - uu = '' + uu = theuser # test to fix bug @@ -2561,7 +2543,7 @@ def otwcp_toadmin_view(request,theuser=0, 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 oterankings_view(request,theuser=0, +def oterankings_view(request,userid=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), startdatestring="", @@ -2584,23 +2566,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 @@ -2674,10 +2644,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 diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 376f563e..2b506203 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -5062,46 +5062,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): From 5e8701792ffdc1814f31bc4d065ea8a8ba891b4b Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 25 Jan 2020 16:33:45 +0100 Subject: [PATCH 55/80] bla --- rowers/views/userviews.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index d3f4903d..d82f5b47 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -5,6 +5,31 @@ from __future__ import unicode_literals from rowers.views.statements import * +@login_required() +def survey(request): + + r = getrower(request.user) + + surveyform = SurveyForm() + + if request.method == 'POST': + form = SurveyForm(request.POST) + r.surveydone = True + r.surveydonedate = timezone.now() + r.save() + + nexturl = request.GET.get('next') + return HttpResponseRedirect(nexturl) + + context = { + 'teams':get_my_teams(request.user), + 'rower':r, + 'form':surveyform, + } + + + return render(request,'survey.html',context) + @login_required() def start_trial_view(request): r = getrower(request.user) From 1ff5ce827f68d3af6ed92d5d893e4e35ff9377de Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 25 Jan 2020 16:46:25 +0100 Subject: [PATCH 56/80] passing a few more tests --- rowers/tests/.~lock.viewnames.csv# | 1 + rowers/urls.py | 6 ++--- rowers/views/analysisviews.py | 42 ++++++++++++++++-------------- 3 files changed, 26 insertions(+), 23 deletions(-) create mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# new file mode 100644 index 00000000..02dab132 --- /dev/null +++ b/rowers/tests/.~lock.viewnames.csv# @@ -0,0 +1 @@ +,sander,sander-pc,25.01.2020 16:35,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/urls.py b/rowers/urls.py index 78072d0d..9bee767c 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -248,13 +248,13 @@ urlpatterns = [ re_path(r'^list-graphs/$',views.graphs_view,name='graphs_view'), re_path(r'^list-graphs/user/(?P\d+)/$',views.graphs_view,name='graphs_view'), re_path(r'^fitness-progress/$',views.fitnessmetric_view,name='fitnessmetric_view'), - re_path(r'^fitness-progress/user/(?P\d+)/$',views.fitnessmetric_view,name='fitnessmetric_view'), - re_path(r'^fitness-progress/user/(?P\d+)/(?P\w+.*)/$',views.fitnessmetric_view,name='fitnessmetric_view'), + re_path(r'^fitness-progress/user/(?P\d+)/$',views.fitnessmetric_view,name='fitnessmetric_view'), + re_path(r'^fitness-progress/user/(?P\d+)/(?P\w+.*)/$',views.fitnessmetric_view,name='fitnessmetric_view'), # re_path(r'^ote-bests/user/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.rankings_view,name='rankings_view'), re_path(r'^ote-bests/user/(?P\d+)/$',views.rankings_view,name='rankings_view'), # re_path(r'^ote-bests/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.rankings_view,name='rankings_view'), re_path(r'^ote-bests/$',views.rankings_view,name='rankings_view'), - re_path(r'^(?P\d+)/ote-bests/$',views.rankings_view,name='rankings_view'), + re_path(r'^(?P\d+)/ote-bests/$',views.rankings_view,name='rankings_view'), # re_path(r'^(?P\d+)/ote-bests2/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.rankings_view2,name='rankings_view2'), re_path(r'^ote-bests2/user/(?P\d+)/$',views.rankings_view2,name='rankings_view2'), # re_path(r'^ote-bests2/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.rankings_view2,name='rankings_view2'), diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index b8a79ad3..03df34f1 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -1335,13 +1335,13 @@ def planrequired_view(request): @user_passes_test(isplanmember,login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) -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 @@ -1391,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, @@ -1412,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 @@ -1741,6 +1742,7 @@ def ajax_agegrouprecords(request, # Show ranking distances including predicted paces @login_required() +@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(), From adaf362fadf3c59bd299db9a6df3e2acc610e3bf Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 25 Jan 2020 16:54:09 +0100 Subject: [PATCH 57/80] a few small fixes --- 646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx | 574 +++++++++++++++++++++++ rowers/urls.py | 2 +- rowers/views/analysisviews.py | 1 + 3 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx diff --git a/646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx b/646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx new file mode 100644 index 00000000..7069443f --- /dev/null +++ b/646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx @@ -0,0 +1,574 @@ +Garmin InternationalExport by rowingdata + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rowers/urls.py b/rowers/urls.py index 9bee767c..faf05629 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -251,7 +251,7 @@ urlpatterns = [ re_path(r'^fitness-progress/user/(?P\d+)/$',views.fitnessmetric_view,name='fitnessmetric_view'), re_path(r'^fitness-progress/user/(?P\d+)/(?P\w+.*)/$',views.fitnessmetric_view,name='fitnessmetric_view'), # re_path(r'^ote-bests/user/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.rankings_view,name='rankings_view'), - re_path(r'^ote-bests/user/(?P\d+)/$',views.rankings_view,name='rankings_view'), + re_path(r'^ote-bests/user/(?P\d+)/$',views.rankings_view,name='rankings_view'), # re_path(r'^ote-bests/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.rankings_view,name='rankings_view'), re_path(r'^ote-bests/$',views.rankings_view,name='rankings_view'), re_path(r'^(?P\d+)/ote-bests/$',views.rankings_view,name='rankings_view'), diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 03df34f1..4048b9ac 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -2179,6 +2179,7 @@ def otwrankings_view(request,userid=0, startdate = s r = getrequestrower(request,userid=userid) + promember = True theuser = r.user From 3633913a5efcfa436d17371e6208c188f80484d4 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 30 Jan 2020 15:12:06 +0100 Subject: [PATCH 58/80] deleting gpx --- 646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx | 574 ----------------------- 1 file changed, 574 deletions(-) delete mode 100644 646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx diff --git a/646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx b/646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx deleted file mode 100644 index 7069443f..00000000 --- a/646902ca-16e5-4ff6-b86a-62ae1dafa415.gpx +++ /dev/null @@ -1,574 +0,0 @@ -Garmin InternationalExport by rowingdata - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 75ceb89a53cff4949887d6a21401d469f7fb75c6 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 30 Jan 2020 17:33:44 +0100 Subject: [PATCH 59/80] a few more views done --- rowers/tests/.~lock.viewnames.csv# | 1 - rowers/tests/viewnames.csv | 32 +++++++++++++++--------------- rowers/urls.py | 2 +- rowers/views/analysisviews.py | 4 ++-- rowers/views/workoutviews.py | 31 ++++++++++------------------- 5 files changed, 30 insertions(+), 40 deletions(-) delete mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# deleted file mode 100644 index 02dab132..00000000 --- a/rowers/tests/.~lock.viewnames.csv# +++ /dev/null @@ -1 +0,0 @@ -,sander,sander-pc,25.01.2020 16:35,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 1611ffa6..d291398d 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -40,22 +40,22 @@ 38,40,rankings_view2,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE 39,41,otwrankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE 40,42,oterankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -41,43,cum_flex,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -42,44,analysis_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -43,47,cum_flex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -44,48,histo,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -45,49,histo_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -46,51,cumstats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -47,53,graph_show_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -48,54,GraphDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -49,55,get_thumbnails,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -50,56,otw_use_impeller,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -51,57,otw_use_gps,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -52,58,workout_toggle_ranking,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -53,59,team_workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -54,60,workout_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -55,61,workout_histo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -56,62,workout_forcecurve_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +41,43,cum_flex,flex all chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +42,44,analysis_view_data,redirects to analysis direct,TRUE,302,pro,302,302,pro,302,302,coach,302,302,FALSE,FALSE,FALSE,TRUE +43,47,cum_flex_data,flex all chart data (json),TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +44,48,histo,histogram view,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE +45,49,histo_data,histogram data,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE +46,51,cumstats,cumulative statistics,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE +47,53,graph_show_view,show a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +48,54,GraphDelete,delete a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE +49,55,get_thumbnails,get thumbnails,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE +50,56,otw_use_impeller,switch to impeller data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE +51,57,otw_use_gps,switch to GPS data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE +52,58,workout_toggle_ranking,toggle ranking,TRUE,302,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE +53,59,team_workout_upload_view,upload workout for team member,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +54,60,workout_upload_view,upload a workout,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE +55,61,workout_histo_view,histogram ,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,TRUE +56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,TRUE 57,63,workout_unsubscribe_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE 58,64,workout_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE 59,65,workout_tcxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE diff --git a/rowers/urls.py b/rowers/urls.py index faf05629..8450a408 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -274,7 +274,7 @@ urlpatterns = [ re_path(r'^flexall/user/(?P\d+)/$',views.cum_flex,name='cum_flex'), re_path(r'^flexall/$',views.cum_flex,name='cum_flex'), re_path(r'^flexalldata/$',views.cum_flex_data,name='cum_flex_data'), - re_path(r'^histo/user/(?P\d+)/$',views.histo,name='histo'), + re_path(r'^histo/user/(?P\d+)/$',views.histo,name='histo'), re_path(r'^histodata/$',views.histo_data,name='histo_data'), # re_path(r'^histo/user/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.histo,name='histo'), re_path(r'^histo/$',views.histo,name='histo'), diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 4048b9ac..67392db8 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -730,7 +730,7 @@ def analysis_view_data(request,userid=0): 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 histo(request,theuser=0, +def histo(request,userid=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), deltadays=-1, @@ -744,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: diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 2b506203..34819d89 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -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.view_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) @@ -503,21 +500,15 @@ def otw_use_gps(request,id=0): # Show Stroke power histogram for a workout @login_required() +@permission_required('workout.view_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] From 4a99d74188ae4e3215663f4b905e2652c1f08c56 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 30 Jan 2020 17:36:32 +0100 Subject: [PATCH 60/80] small fix --- rowers/views/workoutviews.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 34819d89..a0208381 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -386,7 +386,7 @@ 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.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) +@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) @@ -500,7 +500,7 @@ def otw_use_gps(request,id=0): # Show Stroke power histogram for a workout @login_required() -@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) +@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) From f248f5cc28a83c470c218fb64b574a08ab3f5470 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 30 Jan 2020 17:57:36 +0100 Subject: [PATCH 61/80] small bug fixes --- rowers/tests/.~lock.viewnames.csv# | 1 + rowers/tests/viewnames.csv | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# new file mode 100644 index 00000000..2142ac9e --- /dev/null +++ b/rowers/tests/.~lock.viewnames.csv# @@ -0,0 +1 @@ +,sander,sander-pc,30.01.2020 17:54,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index d291398d..0ceaf09e 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -54,7 +54,7 @@ 52,58,workout_toggle_ranking,toggle ranking,TRUE,302,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE 53,59,team_workout_upload_view,upload workout for team member,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE 54,60,workout_upload_view,upload a workout,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -55,61,workout_histo_view,histogram ,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,TRUE +55,61,workout_histo_view,histogram ,TRUE,302,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE 56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,TRUE 57,63,workout_unsubscribe_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE 58,64,workout_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE From 8ef78dc7cea09922e0f5f105f27eabe0276799b2 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 30 Jan 2020 17:57:51 +0100 Subject: [PATCH 62/80] removing temps --- rowers/tests/.~lock.viewnames.csv# | 1 - 1 file changed, 1 deletion(-) delete mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# deleted file mode 100644 index 2142ac9e..00000000 --- a/rowers/tests/.~lock.viewnames.csv# +++ /dev/null @@ -1 +0,0 @@ -,sander,sander-pc,30.01.2020 17:54,file:///home/sander/.config/libreoffice/4; \ No newline at end of file From 3f210f38bf3e48e0a8f1e871d86d240aa5bc77e3 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 1 Feb 2020 09:23:09 +0100 Subject: [PATCH 63/80] two more links tested --- rowers/tests/.~lock.viewnames.csv# | 1 + rowers/tests/viewnames.csv | 624 ++++++++++++++--------------- 2 files changed, 313 insertions(+), 312 deletions(-) create mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# new file mode 100644 index 00000000..5aff9c40 --- /dev/null +++ b/rowers/tests/.~lock.viewnames.csv# @@ -0,0 +1 @@ +,sander,sander-pc,01.02.2020 09:22,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 0ceaf09e..9d4432f9 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -1,312 +1,312 @@ -,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest -0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,TRUE -1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -4,5,fitness_metric_view,updates a person's fitness metric,TRUE,302,basic,302,302,FALSE,403,302,FALSE,403,403,FALSE,FALSE,FALSE,TRUE -5,6,agegrouprecordview,shows ergo age group records,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,403,coach,200,403,FALSE,TRUE,FALSE,TRUE -7,8,virtualevents_view,virtual races list,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -8,9,virtualevent_create_view,create virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -9,10,indoorvirtualevent_create_view,create indoor virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -10,11,virtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -11,12,indoorvirtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -12,13,virtualevent_view,view virtual race,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -13,14,virtualevent_ranking_view,view ranking,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -14,15,virtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -15,16,indoorvirtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -16,17,virtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -17,18,indoorvirtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -18,19,virtualevent_addboat_view,add a new boat type,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -19,20,virtualevent_withdraw_view,withdraw,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -20,21,virtualevent_submit_result_view,submit a result,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,TRUE,FALSE -21,22,virtualevent_disqualify_view,disqualify a racer,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE -22,24,courses_view,see race courses,TRUE,200,FALSE,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -23,25,course_upload_view,upload a new course,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -24,26,addmanual_view,upload a manual workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE -25,27,team_comparison_select,compare with a team member workout,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,TRUE,TRUE,TRUE -26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE -27,29,workouts_join_select,select workouts to join,TRUE,404,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE -28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,TRUE -29,31,analysis_new,analysis front page,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -30,32,user_multiflex_select,select multiflex data,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -31,33,session_jobs_view,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE -32,34,session_jobs_status,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE -33,35,kill_async_job,kill job,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -34,36,post_progress,post progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -35,37,graphs_view,view charts,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -36,38,fitnessmetric_view,view fitness metric,TRUE,302,plan,200,302,plan,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -37,39,rankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -38,40,rankings_view2,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -39,41,otwrankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -40,42,oterankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -41,43,cum_flex,flex all chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -42,44,analysis_view_data,redirects to analysis direct,TRUE,302,pro,302,302,pro,302,302,coach,302,302,FALSE,FALSE,FALSE,TRUE -43,47,cum_flex_data,flex all chart data (json),TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -44,48,histo,histogram view,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -45,49,histo_data,histogram data,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE -46,51,cumstats,cumulative statistics,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE -47,53,graph_show_view,show a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -48,54,GraphDelete,delete a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -49,55,get_thumbnails,get thumbnails,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE -50,56,otw_use_impeller,switch to impeller data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE -51,57,otw_use_gps,switch to GPS data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE -52,58,workout_toggle_ranking,toggle ranking,TRUE,302,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE -53,59,team_workout_upload_view,upload workout for team member,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -54,60,workout_upload_view,upload a workout,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE -55,61,workout_histo_view,histogram ,TRUE,302,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE -56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,TRUE -57,63,workout_unsubscribe_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -58,64,workout_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -59,65,workout_tcxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -60,66,workout_gpxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -61,67,workout_csvemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -62,68,workout_csvtoadmin_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -63,69,workout_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -64,70,workout_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -65,71,workout_update_cp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -66,72,instroke_chart,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -67,73,instroke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -68,74,workout_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -69,75,workout_data_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -70,76,workout_otwsetpower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -71,77,workout_otwpowerplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -72,78,workout_wind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -73,79,workout_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -74,80,virtualevent_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -75,81,virtualevent_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -76,82,virtualevent_setlogo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -77,83,logo_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -78,84,workout_downloadwind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -79,85,workout_downloadmetar_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -80,86,workout_stream_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -81,87,workout_summary_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -82,88,workout_summary_restore_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -83,89,workout_split_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -84,90,workout_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -85,91,workout_video_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -86,92,VideoDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -87,93,workout_video_view_mini,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -88,94,workout_video_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -89,95,list_videos,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -90,96,video_selectworkout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -91,98,workout_fusion_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -92,99,workout_fusion_list,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -93,100,TemplateView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -94,103,workout_add_chart_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -95,112,WorkoutDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -96,113,workout_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -97,114,workout_undo_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -98,115,workout_c2import_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -99,120,workout_stravaimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -100,122,workout_getc2workout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -101,124,workout_getimportview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -102,125,workout_getstravaworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -103,126,workout_getstravaworkout_next,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -104,127,workout_sporttracksimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -105,129,workout_getsporttracksworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -106,130,workout_polarimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -107,132,workout_runkeeperimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -108,134,workout_underarmourimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -109,135,workout_c2_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -110,136,workout_strava_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -111,137,workout_recalcsummary_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -112,138,workout_sporttracks_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -113,139,workout_runkeeper_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -114,140,workout_underarmour_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -115,141,workout_tp_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -116,142,multi_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -119,148,alert_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -120,150,alert_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -121,152,alert_report_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -122,155,boxplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -123,157,boxplot_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -124,158,multiflex_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -125,160,multiflex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -126,161,deactivate_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -127,162,remove_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -128,163,user_gdpr_confirm,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -129,165,user_gdpr_optin,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -130,167,rower_teams_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -131,168,rower_calcdps_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -132,169,rower_exportsettings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -133,171,team_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -134,172,team_members_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -135,173,team_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -136,174,team_leaveconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -137,175,team_leave_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -138,176,team_deleteconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -139,177,team_requestmembership_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -140,178,reject_revoke_coach_request,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -141,179,coach_drop_athlete_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -142,180,coach_drop_athlete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -143,181,athlete_drop_coach_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -144,182,athlete_drop_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -145,184,reject_revoke_coach_offer,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -146,186,request_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -147,187,offer_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -148,188,coach_accept_coachrequest_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -149,189,rower_accept_coachoffer_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -150,190,team_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -151,191,team_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -152,192,manager_member_drop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -153,193,invitation_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -154,194,invitation_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -155,195,rower_invitations_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -156,196,raise_500,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -157,198,request_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -158,199,request_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -159,200,manager_requests_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -160,202,rower_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -161,204,rower_prefs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE -240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE -307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE -310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE +,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest,realtest +0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE +1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +4,5,fitness_metric_view,updates a person's fitness metric,TRUE,302,basic,302,302,FALSE,403,302,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE +5,6,agegrouprecordview,shows ergo age group records,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE +7,8,virtualevents_view,virtual races list,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +8,9,virtualevent_create_view,create virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +9,10,indoorvirtualevent_create_view,create indoor virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +10,11,virtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +11,12,indoorvirtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +12,13,virtualevent_view,view virtual race,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +13,14,virtualevent_ranking_view,view ranking,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +14,15,virtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +15,16,indoorvirtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +16,17,virtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +17,18,indoorvirtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +18,19,virtualevent_addboat_view,add a new boat type,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +19,20,virtualevent_withdraw_view,withdraw,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +20,21,virtualevent_submit_result_view,submit a result,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +21,22,virtualevent_disqualify_view,disqualify a racer,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +22,24,courses_view,see race courses,TRUE,200,FALSE,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +23,25,course_upload_view,upload a new course,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +24,26,addmanual_view,upload a manual workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +25,27,team_comparison_select,compare with a team member workout,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,TRUE,TRUE,FALSE,TRUE +26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE +27,29,workouts_join_select,select workouts to join,TRUE,404,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE +28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +29,31,analysis_new,analysis front page,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +30,32,user_multiflex_select,select multiflex data,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +31,33,session_jobs_view,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +32,34,session_jobs_status,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +33,35,kill_async_job,kill job,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +34,36,post_progress,post progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +35,37,graphs_view,view charts,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +36,38,fitnessmetric_view,view fitness metric,TRUE,302,plan,200,302,plan,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +37,39,rankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +38,40,rankings_view2,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +39,41,otwrankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +40,42,oterankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +41,43,cum_flex,flex all chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +42,44,analysis_view_data,redirects to analysis direct,TRUE,302,pro,302,302,pro,302,302,coach,302,302,FALSE,FALSE,FALSE,FALSE,TRUE +43,47,cum_flex_data,flex all chart data (json),TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +44,48,histo,histogram view,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +45,49,histo_data,histogram data,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +46,51,cumstats,cumulative statistics,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +47,53,graph_show_view,show a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +48,54,GraphDelete,delete a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +49,55,get_thumbnails,get thumbnails,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +50,56,otw_use_impeller,switch to impeller data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE +51,57,otw_use_gps,switch to GPS data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE +52,58,workout_toggle_ranking,toggle ranking,TRUE,302,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE +53,59,team_workout_upload_view,upload workout for team member,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +54,60,workout_upload_view,upload a workout,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +55,61,workout_histo_view,histogram ,TRUE,302,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +57,63,workout_unsubscribe_view,unsubscribe from comments,TRUE,302,basic,200,302,basic,200,302,basic,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +58,64,workout_comment_view,comment on workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +59,65,workout_tcxemail_view,download TCX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,FALSE +60,66,workout_gpxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +61,67,workout_csvemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +62,68,workout_csvtoadmin_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +63,69,workout_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +64,70,workout_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +65,71,workout_update_cp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +66,72,instroke_chart,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +67,73,instroke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +68,74,workout_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +69,75,workout_data_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +70,76,workout_otwsetpower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +71,77,workout_otwpowerplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +72,78,workout_wind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +73,79,workout_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +74,80,virtualevent_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +75,81,virtualevent_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +76,82,virtualevent_setlogo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +77,83,logo_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +78,84,workout_downloadwind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +79,85,workout_downloadmetar_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +80,86,workout_stream_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +81,87,workout_summary_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +82,88,workout_summary_restore_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +83,89,workout_split_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +84,90,workout_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +85,91,workout_video_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +86,92,VideoDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +87,93,workout_video_view_mini,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +88,94,workout_video_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +89,95,list_videos,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +90,96,video_selectworkout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +91,98,workout_fusion_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +92,99,workout_fusion_list,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +93,100,TemplateView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +94,103,workout_add_chart_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +95,112,WorkoutDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +96,113,workout_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +97,114,workout_undo_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +98,115,workout_c2import_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +99,120,workout_stravaimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +100,122,workout_getc2workout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +101,124,workout_getimportview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +102,125,workout_getstravaworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +103,126,workout_getstravaworkout_next,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +104,127,workout_sporttracksimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +105,129,workout_getsporttracksworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +106,130,workout_polarimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +107,132,workout_runkeeperimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +108,134,workout_underarmourimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +109,135,workout_c2_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +110,136,workout_strava_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +111,137,workout_recalcsummary_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +112,138,workout_sporttracks_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +113,139,workout_runkeeper_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +114,140,workout_underarmour_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +115,141,workout_tp_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +116,142,multi_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +119,148,alert_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +120,150,alert_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +121,152,alert_report_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +122,155,boxplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +123,157,boxplot_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +124,158,multiflex_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +125,160,multiflex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +126,161,deactivate_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +127,162,remove_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +128,163,user_gdpr_confirm,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +129,165,user_gdpr_optin,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +130,167,rower_teams_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +131,168,rower_calcdps_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +132,169,rower_exportsettings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +133,171,team_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +134,172,team_members_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +135,173,team_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +136,174,team_leaveconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +137,175,team_leave_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +138,176,team_deleteconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +139,177,team_requestmembership_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +140,178,reject_revoke_coach_request,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +141,179,coach_drop_athlete_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +142,180,coach_drop_athlete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +143,181,athlete_drop_coach_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +144,182,athlete_drop_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +145,184,reject_revoke_coach_offer,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +146,186,request_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +147,187,offer_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +148,188,coach_accept_coachrequest_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +149,189,rower_accept_coachoffer_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +150,190,team_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +151,191,team_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +152,192,manager_member_drop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +153,193,invitation_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +154,194,invitation_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +155,195,rower_invitations_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +156,196,raise_500,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +157,198,request_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +158,199,request_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +159,200,manager_requests_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +160,202,rower_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +161,204,rower_prefs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE +240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE From 122aff320f47aaf9ca5a17a816bb828a6e82db38 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 1 Feb 2020 11:33:21 +0100 Subject: [PATCH 64/80] A few more fixes --- rowers/rower_rules.py | 5 ++ rowers/tests/.~lock.viewnames.csv# | 2 +- rowers/tests/viewnames.csv | 12 ++--- rowers/views/exportviews.py | 1 + rowers/views/paymentviews.py | 84 +++++++++++++++--------------- 5 files changed, 55 insertions(+), 49 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 93bbdcd7..924376bf 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -98,6 +98,10 @@ def can_start_plantrial(user): return user.rower.plantrialexpires == datetime.date(1970,1,1) +@rules.predicate +def is_staff(user): + return user.is_staff + @rules.predicate def is_coach(user): return user.rower.rowerplan in ['coach','freecoach'] @@ -274,6 +278,7 @@ def can_plan_user(user,rower): rules.add_perm('rower.add_plan',can_plan_user) # replaces checkaccessplanuser rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser rules.add_perm('rower.is_pro',ispromember) +rules.add_perm('rower.is_staff',is_staff) # WORKOUT permissions diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# index 5aff9c40..c1247cdc 100644 --- a/rowers/tests/.~lock.viewnames.csv# +++ b/rowers/tests/.~lock.viewnames.csv# @@ -1 +1 @@ -,sander,sander-pc,01.02.2020 09:22,file:///home/sander/.config/libreoffice/4; \ No newline at end of file +,sander,sander-pc,01.02.2020 11:32,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 9d4432f9..18cbf41a 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -58,12 +58,12 @@ 56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE 57,63,workout_unsubscribe_view,unsubscribe from comments,TRUE,302,basic,200,302,basic,200,302,basic,200,302,FALSE,FALSE,TRUE,FALSE,TRUE 58,64,workout_comment_view,comment on workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -59,65,workout_tcxemail_view,download TCX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,FALSE -60,66,workout_gpxemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -61,67,workout_csvemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -62,68,workout_csvtoadmin_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -63,69,workout_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -64,70,workout_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +59,65,workout_tcxemail_view,download TCX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE +60,66,workout_gpxemail_view,download GPX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE +61,67,workout_csvemail_view,download CSV file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE +62,68,workout_csvtoadmin_view,send CSV to admin,TRUE,403,basic,200,200,basic,200,200,coach,200,200,TRUE,FALSE,TRUE,FALSE,TRUE +63,69,workout_edit_view,Edit Workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE +64,70,workout_map_view,View workout Map,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE 65,71,workout_update_cp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 66,72,instroke_chart,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE 67,73,instroke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE diff --git a/rowers/views/exportviews.py b/rowers/views/exportviews.py index afdfc7c9..b415696a 100644 --- a/rowers/views/exportviews.py +++ b/rowers/views/exportviews.py @@ -261,6 +261,7 @@ def workout_csvemail_view(request,id=0): # 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/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 }) - From 497b6204d068f35757ed97b92128543b89da91ff Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 1 Feb 2020 11:45:01 +0100 Subject: [PATCH 65/80] up to stats --- rowers/tests/.~lock.viewnames.csv# | 1 - rowers/tests/viewnames.csv | 10 +++++----- rowers/views/workoutviews.py | 1 + 3 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# deleted file mode 100644 index c1247cdc..00000000 --- a/rowers/tests/.~lock.viewnames.csv# +++ /dev/null @@ -1 +0,0 @@ -,sander,sander-pc,01.02.2020 11:32,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 18cbf41a..f36066ca 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -63,11 +63,11 @@ 61,67,workout_csvemail_view,download CSV file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE 62,68,workout_csvtoadmin_view,send CSV to admin,TRUE,403,basic,200,200,basic,200,200,coach,200,200,TRUE,FALSE,TRUE,FALSE,TRUE 63,69,workout_edit_view,Edit Workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -64,70,workout_map_view,View workout Map,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE -65,71,workout_update_cp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -66,72,instroke_chart,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -67,73,instroke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -68,74,workout_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +64,70,workout_map_view,View workout Map,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +65,71,workout_update_cp_view,Update CP data based on new workout,TRUE,403,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE +66,72,instroke_chart,View In-Stroke data chart,TRUE,302,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,FALSE,FALSE,FALSE +67,73,instroke_view,Create in stroke chart,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +68,74,workout_stats_view,View Workout Stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE 69,75,workout_data_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 70,76,workout_otwsetpower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 71,77,workout_otwpowerplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index a0208381..7ccce1be 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -736,6 +736,7 @@ def fitness_metric_view(request,mode='rower',days=42): @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) From 2e145a737c9376fdf8205f6eed783357ad51e69c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 1 Feb 2020 15:40:06 +0100 Subject: [PATCH 66/80] adding --- rowers/templates/plannedsessioncreate.html | 2 +- rowers/views/planviews.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rowers/templates/plannedsessioncreate.html b/rowers/templates/plannedsessioncreate.html index fc6ac992..38f29545 100644 --- a/rowers/templates/plannedsessioncreate.html +++ b/rowers/templates/plannedsessioncreate.html @@ -130,7 +130,7 @@ Edit Template - Delete + Delete {% endfor %} diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 48985c74..b5ec6dad 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1502,7 +1502,8 @@ def plannedsession_teamclone_view(request,id=0): next = request.GET.get('next', url) return HttpResponseRedirect(next) -@user_passes_test(hasplannedsessions, login_url="/rowers/paidplans/", +@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) def plannedsession_templateedit_view(request,id=0): @@ -1989,8 +1990,9 @@ 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) From 0366e8ee69abf997c00b289f667aed847edb2fed Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 2 Feb 2020 16:45:40 +0100 Subject: [PATCH 67/80] a few more views checked --- rowers/tests/.~lock.viewnames.csv# | 1 + rowers/tests/viewnames.csv | 53 +++++++++++++++--------------- rowers/urls.py | 1 + rowers/views/workoutviews.py | 33 +++++++++---------- 4 files changed, 44 insertions(+), 44 deletions(-) create mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# new file mode 100644 index 00000000..bd542c4c --- /dev/null +++ b/rowers/tests/.~lock.viewnames.csv# @@ -0,0 +1 @@ +,sander,sander-pc,02.02.2020 16:37,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index f36066ca..0d67bacc 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -67,33 +67,32 @@ 65,71,workout_update_cp_view,Update CP data based on new workout,TRUE,403,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE 66,72,instroke_chart,View In-Stroke data chart,TRUE,302,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,FALSE,FALSE,FALSE 67,73,instroke_view,Create in stroke chart,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -68,74,workout_stats_view,View Workout Stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE -69,75,workout_data_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -70,76,workout_otwsetpower_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -71,77,workout_otwpowerplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -72,78,workout_wind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -73,79,workout_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -74,80,virtualevent_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -75,81,virtualevent_uploadimage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -76,82,virtualevent_setlogo_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -77,83,logo_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -78,84,workout_downloadwind_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -79,85,workout_downloadmetar_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -80,86,workout_stream_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -81,87,workout_summary_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -82,88,workout_summary_restore_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -83,89,workout_split_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -84,90,workout_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -85,91,workout_video_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -86,92,VideoDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -87,93,workout_video_view_mini,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -88,94,workout_video_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -89,95,list_videos,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -90,96,video_selectworkout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -91,98,workout_fusion_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -92,99,workout_fusion_list,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -93,100,TemplateView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -94,103,workout_add_chart_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +68,74,workout_stats_view,View Workout Stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +69,75,workout_data_view,shows the detailed data for a workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE +70,76,workout_otwsetpower_view,set weight and start offline OTW power calculations,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE +71,77,workout_otwpowerplot_view,generates OTW power chart,TRUE,200,basic,200,200,basic,200,200,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +72,78,workout_wind_view,set wind,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE +73,79,workout_uploadimage_view,upload image,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE +74,80,virtualevent_compare_view,compare workouts from a virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +75,81,virtualevent_uploadimage_view,upload image to virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +76,82,virtualevent_setlogo_view,set logo of virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +77,83,logo_delete_view,delete logo,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +78,84,workout_downloadwind_view,download Wind,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,FALSE +79,85,workout_downloadmetar_view,download METAR,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,FALSE +80,86,workout_stream_view,edit stream (redirects as no data in test suite),TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +81,87,workout_summary_edit_view,edit workout summary,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE +82,88,workout_summary_restore_view,restore workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE +83,89,workout_split_view,split workout,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +84,90,workout_view,view workout ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +85,91,workout_video_create_view,add video ,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +86,92,VideoDelete,delete video,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +87,93,workout_video_view_mini,minimal view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +88,94,workout_video_view,"video view (not tested, no video in test suite)",TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +89,95,list_videos,list my videos,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE +90,96,video_selectworkout,select workout to create video,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +91,98,workout_fusion_view,needs to workouts,TRUE,403,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +92,99,workout_fusion_list,list workouts to fuse with,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE +94,103,workout_add_chart_view,add chart,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,FALSE 95,112,WorkoutDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE 96,113,workout_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 97,114,workout_undo_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE diff --git a/rowers/urls.py b/rowers/urls.py index dc813203..d07c68e9 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -357,6 +357,7 @@ urlpatterns = [ re_path(r'^video/(?P\w.+)/$',views.workout_video_view, name='workout_video_view'), re_path(r'^videos/',views.list_videos,name='list_videos'), + re_path(r'^add-video/user/(?P\d+)/$',views.video_selectworkout,name='video_selectworkout'), re_path(r'^add-video/',views.video_selectworkout,name='video_selectworkout'), # re_path(r'^workout/(?P\d+)/$',views.workout_view,name='workout_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/$',views.workout_view,name='workout_view'), diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 7ccce1be..a52d31e4 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -846,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 @@ -1967,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(): @@ -2005,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, @@ -2034,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 = [ { @@ -2610,7 +2609,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 @@ -3702,14 +3703,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) From cc85e920be86f6054bbbe99511db204a97f49802 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 2 Feb 2020 16:45:55 +0100 Subject: [PATCH 68/80] removing temp file --- rowers/tests/.~lock.viewnames.csv# | 1 - 1 file changed, 1 deletion(-) delete mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# deleted file mode 100644 index bd542c4c..00000000 --- a/rowers/tests/.~lock.viewnames.csv# +++ /dev/null @@ -1 +0,0 @@ -,sander,sander-pc,02.02.2020 16:37,file:///home/sander/.config/libreoffice/4; \ No newline at end of file From 2c0ae3f135274a9bdde87458f0ecfa63ac3c64d0 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 3 Feb 2020 08:56:03 +0100 Subject: [PATCH 69/80] local changes --- rowers/rower_rules.py | 29 +++++++++++++++++++++++++++++ rowers/tests/viewnames.csv | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 93bbdcd7..df9892e6 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -217,6 +217,34 @@ def is_coach_user(usercoach,userrower): return False +# checks if rower is coach of user (or is user himself) +@rules.predicate +def is_anonymous_or_coach(usercoach,userrower): + print(usercoach,userrower) + if usercoach == userrower: + return True + + if userrower.is_anonymous and userrower.is_anonymous: + return True + + if not is_coach(usercoach): + return False + + r = userrower.rower + + coaches = [] + + for group in r.coachinggroups.all(): + newcoaches = group.get_coaches() + for coach in newcoaches: + coaches.append(coach) + + for coach in coaches: + if usercoach.rower == coach: + return True + + return False + # check if rower and user are members of the same team @rules.predicate def is_rower_team_member(user,rower): @@ -274,6 +302,7 @@ def can_plan_user(user,rower): rules.add_perm('rower.add_plan',can_plan_user) # replaces checkaccessplanuser rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser rules.add_perm('rower.is_pro',ispromember) +rules.add_perm('anonymous.is_coach',is_anonymous_or_coach) # WORKOUT permissions diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 4b609ebd..1978647e 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -28,7 +28,7 @@ 26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE 27,29,workouts_join_select,select workouts to join,TRUE,404,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE 28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,TRUE -29,31,analysis_new,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE +29,31,analysis_new,front page for analysis section,TRUE,200,basic,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE 30,32,user_multiflex_select,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE 31,33,session_jobs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE 32,34,session_jobs_status,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE From 2350e33bbd83c034a87fe503f85c952a6f068876 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 3 Feb 2020 17:22:57 +0100 Subject: [PATCH 70/80] some more links tested --- rowers/tests/mocks.py | 4 +++ rowers/tests/test_permissions2.py | 16 +++++++++++ rowers/tests/viewnames.csv | 44 +++++++++++++++---------------- rowers/urls.py | 1 + rowers/views/importviews.py | 16 +++++++---- 5 files changed, 54 insertions(+), 27 deletions(-) diff --git a/rowers/tests/mocks.py b/rowers/tests/mocks.py index 7acce340..d725b8b5 100644 --- a/rowers/tests/mocks.py +++ b/rowers/tests/mocks.py @@ -69,6 +69,10 @@ def mocked_myqueue(*args, **kwargs): return Job() +def mock_c2open(*args, **kwargs): + print('mock') + return('aap') + def mocked_sqlalchemy(*args, **kwargs): # return object with method diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index f9ebb810..4fc84ad7 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -214,6 +214,8 @@ class PermissionsViewTests(TestCase): # Test access for anonymous users @parameterized.expand(viewstotest) + @patch('rowers.c2stuff.Session', side_effect=mocked_requests) + @patch('rowers.c2stuff.c2_open') @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.read_df_sql') @patch('rowers.dataprep.getsmallrowdata_db') @@ -222,6 +224,8 @@ class PermissionsViewTests(TestCase): @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) def test_permissions_anonymous( self,view,permissions, + mock_Session, + mock_c2open, mocked_sqlalchemy, mocked_read_df_sql, mocked_getsmallrowdata_db, @@ -251,6 +255,8 @@ class PermissionsViewTests(TestCase): # Test access for logged in users - accessing own objects @parameterized.expand(viewstotest) + @patch('rowers.c2stuff.Session', side_effect=mocked_requests) + @patch('rowers.c2stuff.c2_open') @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.read_df_sql') @patch('rowers.dataprep.getsmallrowdata_db') @@ -259,6 +265,8 @@ class PermissionsViewTests(TestCase): @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) def test_permissions_own( self,view,permissions, + mock_Session, + mock_c2open, mocked_sqlalchemy, mocked_read_df_sql, mocked_getsmallrowdata_db, @@ -337,6 +345,8 @@ class PermissionsViewTests(TestCase): # Test access for logged in users - accessing team member objects @parameterized.expand(viewstotest) + @patch('rowers.c2stuff.Session', side_effect=mocked_requests) + @patch('rowers.c2stuff.c2_open') @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.read_df_sql') @patch('rowers.dataprep.getsmallrowdata_db') @@ -345,6 +355,8 @@ class PermissionsViewTests(TestCase): @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) def test_permissions_member( self,view,permissions, + mock_Session, + mock_c2open, mocked_sqlalchemy, mocked_read_df_sql, mocked_getsmallrowdata_db, @@ -424,6 +436,8 @@ class PermissionsViewTests(TestCase): # Test access for logged in users - accessing coachee @parameterized.expand(viewstotest) + @patch('rowers.c2stuff.Session', side_effect=mocked_requests) + @patch('rowers.c2stuff.c2_open') @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.read_df_sql') @patch('rowers.dataprep.getsmallrowdata_db') @@ -432,6 +446,8 @@ class PermissionsViewTests(TestCase): @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) def test_permissions_coachee( self,view,permissions, + mock_Session, + mock_c2open, mocked_sqlalchemy, mocked_read_df_sql, mocked_getsmallrowdata_db, diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 0d67bacc..825bf77c 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -92,28 +92,28 @@ 90,96,video_selectworkout,select workout to create video,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE 91,98,workout_fusion_view,needs to workouts,TRUE,403,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE 92,99,workout_fusion_list,list workouts to fuse with,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -94,103,workout_add_chart_view,add chart,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,FALSE -95,112,WorkoutDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -96,113,workout_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -97,114,workout_undo_smoothenpace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -98,115,workout_c2import_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -99,120,workout_stravaimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -100,122,workout_getc2workout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -101,124,workout_getimportview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -102,125,workout_getstravaworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -103,126,workout_getstravaworkout_next,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -104,127,workout_sporttracksimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -105,129,workout_getsporttracksworkout_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -106,130,workout_polarimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -107,132,workout_runkeeperimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -108,134,workout_underarmourimport_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -109,135,workout_c2_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -110,136,workout_strava_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -111,137,workout_recalcsummary_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -112,138,workout_sporttracks_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -113,139,workout_runkeeper_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -114,140,workout_underarmour_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -115,141,workout_tp_upload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +94,103,workout_add_chart_view,add chart,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,FALSE +95,112,WorkoutDelete,delete workout,TRUE,403,basic,200,302,basic,403,403,coach,403,403,FALSE,FALSE,TRUE,FALSE,TRUE +96,113,workout_smoothenpace_view,smoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE +97,114,workout_undo_smoothenpace_view,unsmoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE +98,115,workout_c2import_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE +99,120,workout_stravaimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE +100,122,workout_getc2workout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE +101,124,workout_getimportview,imports a workout from third party,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +102,125,workout_getstravaworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE +103,126,workout_getstravaworkout_next,gets all strava workouts,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +104,127,workout_sporttracksimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE +105,129,workout_getsporttracksworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE +106,130,workout_polarimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE +107,132,workout_runkeeperimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE +108,134,workout_underarmourimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE +109,135,workout_c2_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +110,136,workout_strava_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +111,137,workout_recalcsummary_view,recalculates workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,FALSE +112,138,workout_sporttracks_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +113,139,workout_runkeeper_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +114,140,workout_underarmour_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +115,141,workout_tp_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 116,142,multi_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE 118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE diff --git a/rowers/urls.py b/rowers/urls.py index d07c68e9..7735e0b6 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -421,6 +421,7 @@ urlpatterns = [ re_path(r'^workout/polarimport/user/(?P\d+)/',views.workout_polarimport_view,name='workout_polarimport_view'), re_path(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view,name='workout_runkeeperimport_view'), re_path(r'^workout/runkeeperimport/user/(?P\d+)/$',views.workout_runkeeperimport_view,name='workout_runkeeperimport_view'), + re_path(r'^workout/underarmourimport/user/(?P\d+)/$',views.workout_underarmourimport_view,name='workout_underarmourimport_view'), re_path(r'^workout/underarmourimport/$',views.workout_underarmourimport_view,name='workout_underarmourimport_view'), # re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/deleteconfirm/$',views.workout_delete_confirm_view), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/c2uploadw/$',views.workout_c2_upload_view,name='workout_c2_upload_view'), diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index a823c788..8e9ecec4 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -886,10 +886,11 @@ def rower_process_testcallback(request): # 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) @@ -985,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): @@ -1040,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): @@ -1090,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 = [] @@ -1147,6 +1151,7 @@ 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): @@ -1291,14 +1296,15 @@ def workout_getc2workout_all(request,page=1,message=""): # 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) From db52894fe9e24c36adeea04eb6abba68f9a55a6b Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 3 Feb 2020 21:47:39 +0100 Subject: [PATCH 71/80] multi compare --- rowers/tests/.~lock.viewnames.csv# | 1 + rowers/tests/viewnames.csv | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# new file mode 100644 index 00000000..336591e1 --- /dev/null +++ b/rowers/tests/.~lock.viewnames.csv# @@ -0,0 +1 @@ +,sander,sander-pc,03.02.2020 21:47,file:///home/sander/.config/libreoffice/4; \ No newline at end of file diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 825bf77c..bd8d253e 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -114,7 +114,7 @@ 113,139,workout_runkeeper_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 114,140,workout_underarmour_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 115,141,workout_tp_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -116,142,multi_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +116,142,multi_compare_view,compare workouts ,TRUE,302,basic,302,403,basic,302,403,coach,302,302,FALSE,FALSE,TRUE,TRUE,TRUE 117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE 118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE 119,148,alert_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE From 77bb4dd8ee888e1a9ec7e64ad0feb983dbf5538e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 3 Feb 2020 21:48:03 +0100 Subject: [PATCH 72/80] remove unnessecary files --- rowers/tests/.~lock.viewnames.csv# | 1 - 1 file changed, 1 deletion(-) delete mode 100644 rowers/tests/.~lock.viewnames.csv# diff --git a/rowers/tests/.~lock.viewnames.csv# b/rowers/tests/.~lock.viewnames.csv# deleted file mode 100644 index 336591e1..00000000 --- a/rowers/tests/.~lock.viewnames.csv# +++ /dev/null @@ -1 +0,0 @@ -,sander,sander-pc,03.02.2020 21:47,file:///home/sander/.config/libreoffice/4; \ No newline at end of file From 04537d2044a349ca99401cfe85b5469c5f989025 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 4 Feb 2020 18:14:07 +0100 Subject: [PATCH 73/80] viewnames --- rowers/tests/viewnames.csv | 20 ++++++++++---------- rowers/views/analysisviews.py | 1 + 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index bd8d253e..a58ac41f 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -114,16 +114,16 @@ 113,139,workout_runkeeper_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 114,140,workout_underarmour_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE 115,141,workout_tp_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -116,142,multi_compare_view,compare workouts ,TRUE,302,basic,302,403,basic,302,403,coach,302,302,FALSE,FALSE,TRUE,TRUE,TRUE -117,145,alerts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -118,147,AlertDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -119,148,alert_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -120,150,alert_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -121,152,alert_report_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -122,155,boxplot_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -123,157,boxplot_view_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -124,158,multiflex_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -125,160,multiflex_data,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +116,142,multi_compare_view,compare workouts ,TRUE,302,basic,302,403,basic,302,403,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE +117,145,alerts_view,view alerts,TRUE,302,basic,200,403,basic,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE +118,147,AlertDelete,delete alert,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +119,148,alert_edit_view,edit alert,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE +120,150,alert_create_view,create alert,TRUE,302,pro,200,403,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE +121,152,alert_report_view,view alerts report,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE +122,155,boxplot_view,View boxplot,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE +123,157,boxplot_view_data,data for boxplot,TRUE,302,pro,200,403,pro,200,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE +124,158,multiflex_view,view multiflex,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE +125,160,multiflex_data,data for multiflex,TRUE,302,pro,200,403,pro,200,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE 126,161,deactivate_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE 127,162,remove_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE 128,163,user_gdpr_confirm,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 67392db8..6d09525e 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -4310,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) From ebb62dfb9d57c721912c97aadaed404bd66ae65b Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 5 Feb 2020 17:43:33 +0100 Subject: [PATCH 74/80] a few more links checked --- rowers/tests/viewnames.csv | 622 ++++++++++++++++++------------------- rowers/urls.py | 6 +- rowers/views/otherviews.py | 128 +------- rowers/views/teamviews.py | 6 +- rowers/views/userviews.py | 135 +++++++- 5 files changed, 449 insertions(+), 448 deletions(-) diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index a58ac41f..62375b71 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -1,311 +1,311 @@ -,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest,realtest -0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE -1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -4,5,fitness_metric_view,updates a person's fitness metric,TRUE,302,basic,302,302,FALSE,403,302,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE -5,6,agegrouprecordview,shows ergo age group records,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE -7,8,virtualevents_view,virtual races list,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -8,9,virtualevent_create_view,create virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -9,10,indoorvirtualevent_create_view,create indoor virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -10,11,virtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -11,12,indoorvirtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -12,13,virtualevent_view,view virtual race,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -13,14,virtualevent_ranking_view,view ranking,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -14,15,virtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -15,16,indoorvirtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -16,17,virtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -17,18,indoorvirtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -18,19,virtualevent_addboat_view,add a new boat type,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -19,20,virtualevent_withdraw_view,withdraw,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -20,21,virtualevent_submit_result_view,submit a result,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -21,22,virtualevent_disqualify_view,disqualify a racer,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -22,24,courses_view,see race courses,TRUE,200,FALSE,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -23,25,course_upload_view,upload a new course,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -24,26,addmanual_view,upload a manual workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -25,27,team_comparison_select,compare with a team member workout,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,TRUE,TRUE,FALSE,TRUE -26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE -27,29,workouts_join_select,select workouts to join,TRUE,404,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE -28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -29,31,analysis_new,analysis front page,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -30,32,user_multiflex_select,select multiflex data,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -31,33,session_jobs_view,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -32,34,session_jobs_status,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -33,35,kill_async_job,kill job,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -34,36,post_progress,post progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -35,37,graphs_view,view charts,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -36,38,fitnessmetric_view,view fitness metric,TRUE,302,plan,200,302,plan,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -37,39,rankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -38,40,rankings_view2,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -39,41,otwrankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -40,42,oterankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -41,43,cum_flex,flex all chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -42,44,analysis_view_data,redirects to analysis direct,TRUE,302,pro,302,302,pro,302,302,coach,302,302,FALSE,FALSE,FALSE,FALSE,TRUE -43,47,cum_flex_data,flex all chart data (json),TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -44,48,histo,histogram view,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -45,49,histo_data,histogram data,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -46,51,cumstats,cumulative statistics,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -47,53,graph_show_view,show a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -48,54,GraphDelete,delete a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -49,55,get_thumbnails,get thumbnails,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -50,56,otw_use_impeller,switch to impeller data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE -51,57,otw_use_gps,switch to GPS data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE -52,58,workout_toggle_ranking,toggle ranking,TRUE,302,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE -53,59,team_workout_upload_view,upload workout for team member,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -54,60,workout_upload_view,upload a workout,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -55,61,workout_histo_view,histogram ,TRUE,302,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -57,63,workout_unsubscribe_view,unsubscribe from comments,TRUE,302,basic,200,302,basic,200,302,basic,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -58,64,workout_comment_view,comment on workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -59,65,workout_tcxemail_view,download TCX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -60,66,workout_gpxemail_view,download GPX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -61,67,workout_csvemail_view,download CSV file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -62,68,workout_csvtoadmin_view,send CSV to admin,TRUE,403,basic,200,200,basic,200,200,coach,200,200,TRUE,FALSE,TRUE,FALSE,TRUE -63,69,workout_edit_view,Edit Workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -64,70,workout_map_view,View workout Map,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -65,71,workout_update_cp_view,Update CP data based on new workout,TRUE,403,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE -66,72,instroke_chart,View In-Stroke data chart,TRUE,302,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,FALSE,FALSE,FALSE -67,73,instroke_view,Create in stroke chart,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -68,74,workout_stats_view,View Workout Stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -69,75,workout_data_view,shows the detailed data for a workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -70,76,workout_otwsetpower_view,set weight and start offline OTW power calculations,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -71,77,workout_otwpowerplot_view,generates OTW power chart,TRUE,200,basic,200,200,basic,200,200,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -72,78,workout_wind_view,set wind,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -73,79,workout_uploadimage_view,upload image,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -74,80,virtualevent_compare_view,compare workouts from a virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -75,81,virtualevent_uploadimage_view,upload image to virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -76,82,virtualevent_setlogo_view,set logo of virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -77,83,logo_delete_view,delete logo,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -78,84,workout_downloadwind_view,download Wind,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,FALSE -79,85,workout_downloadmetar_view,download METAR,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,FALSE -80,86,workout_stream_view,edit stream (redirects as no data in test suite),TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -81,87,workout_summary_edit_view,edit workout summary,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE -82,88,workout_summary_restore_view,restore workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE -83,89,workout_split_view,split workout,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -84,90,workout_view,view workout ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -85,91,workout_video_create_view,add video ,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -86,92,VideoDelete,delete video,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -87,93,workout_video_view_mini,minimal view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -88,94,workout_video_view,"video view (not tested, no video in test suite)",TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -89,95,list_videos,list my videos,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE -90,96,video_selectworkout,select workout to create video,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -91,98,workout_fusion_view,needs to workouts,TRUE,403,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -92,99,workout_fusion_list,list workouts to fuse with,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE -94,103,workout_add_chart_view,add chart,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,FALSE -95,112,WorkoutDelete,delete workout,TRUE,403,basic,200,302,basic,403,403,coach,403,403,FALSE,FALSE,TRUE,FALSE,TRUE -96,113,workout_smoothenpace_view,smoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE -97,114,workout_undo_smoothenpace_view,unsmoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE -98,115,workout_c2import_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE -99,120,workout_stravaimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE -100,122,workout_getc2workout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE -101,124,workout_getimportview,imports a workout from third party,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -102,125,workout_getstravaworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE -103,126,workout_getstravaworkout_next,gets all strava workouts,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -104,127,workout_sporttracksimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE -105,129,workout_getsporttracksworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE -106,130,workout_polarimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE -107,132,workout_runkeeperimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE -108,134,workout_underarmourimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE -109,135,workout_c2_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -110,136,workout_strava_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -111,137,workout_recalcsummary_view,recalculates workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,FALSE -112,138,workout_sporttracks_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -113,139,workout_runkeeper_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -114,140,workout_underarmour_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -115,141,workout_tp_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -116,142,multi_compare_view,compare workouts ,TRUE,302,basic,302,403,basic,302,403,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE -117,145,alerts_view,view alerts,TRUE,302,basic,200,403,basic,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE -118,147,AlertDelete,delete alert,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -119,148,alert_edit_view,edit alert,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -120,150,alert_create_view,create alert,TRUE,302,pro,200,403,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE -121,152,alert_report_view,view alerts report,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE -122,155,boxplot_view,View boxplot,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE -123,157,boxplot_view_data,data for boxplot,TRUE,302,pro,200,403,pro,200,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE -124,158,multiflex_view,view multiflex,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE -125,160,multiflex_data,data for multiflex,TRUE,302,pro,200,403,pro,200,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE -126,161,deactivate_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -127,162,remove_user,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -128,163,user_gdpr_confirm,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -129,165,user_gdpr_optin,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -130,167,rower_teams_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -131,168,rower_calcdps_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -132,169,rower_exportsettings_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -133,171,team_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -134,172,team_members_stats_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -135,173,team_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -136,174,team_leaveconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -137,175,team_leave_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -138,176,team_deleteconfirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -139,177,team_requestmembership_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -140,178,reject_revoke_coach_request,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -141,179,coach_drop_athlete_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -142,180,coach_drop_athlete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -143,181,athlete_drop_coach_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -144,182,athlete_drop_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -145,184,reject_revoke_coach_offer,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -146,186,request_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -147,187,offer_coaching_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -148,188,coach_accept_coachrequest_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -149,189,rower_accept_coachoffer_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -150,190,team_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -151,191,team_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -152,192,manager_member_drop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -153,193,invitation_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -154,194,invitation_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -155,195,rower_invitations_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -156,196,raise_500,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -157,198,request_revoke_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -158,199,request_reject_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -159,200,manager_requests_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -160,202,rower_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -161,204,rower_prefs_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE -240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE -307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE -310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE +,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest,realtest,kwargs +0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, +1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +4,5,fitness_metric_view,updates a person's fitness metric,TRUE,302,basic,302,302,FALSE,403,302,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, +5,6,agegrouprecordview,shows ergo age group records,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +7,8,virtualevents_view,virtual races list,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +8,9,virtualevent_create_view,create virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +9,10,indoorvirtualevent_create_view,create indoor virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +10,11,virtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +11,12,indoorvirtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +12,13,virtualevent_view,view virtual race,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +13,14,virtualevent_ranking_view,view ranking,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +14,15,virtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +15,16,indoorvirtualevent_edit_view,edit race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +16,17,virtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +17,18,indoorvirtualevent_register_view,register for race,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +18,19,virtualevent_addboat_view,add a new boat type,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +19,20,virtualevent_withdraw_view,withdraw,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +20,21,virtualevent_submit_result_view,submit a result,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +21,22,virtualevent_disqualify_view,disqualify a racer,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +22,24,courses_view,see race courses,TRUE,200,FALSE,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +23,25,course_upload_view,upload a new course,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +24,26,addmanual_view,upload a manual workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +25,27,team_comparison_select,compare with a team member workout,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,TRUE,TRUE,FALSE,TRUE, +26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +27,29,workouts_join_select,select workouts to join,TRUE,404,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +29,31,analysis_new,analysis front page,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +30,32,user_multiflex_select,select multiflex data,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +31,33,session_jobs_view,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +32,34,session_jobs_status,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +33,35,kill_async_job,kill job,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +34,36,post_progress,post progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +35,37,graphs_view,view charts,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +36,38,fitnessmetric_view,view fitness metric,TRUE,302,plan,200,302,plan,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +37,39,rankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +38,40,rankings_view2,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +39,41,otwrankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +40,42,oterankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +41,43,cum_flex,flex all chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +42,44,analysis_view_data,redirects to analysis direct,TRUE,302,pro,302,302,pro,302,302,coach,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, +43,47,cum_flex_data,flex all chart data (json),TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +44,48,histo,histogram view,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +45,49,histo_data,histogram data,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +46,51,cumstats,cumulative statistics,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +47,53,graph_show_view,show a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +48,54,GraphDelete,delete a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +49,55,get_thumbnails,get thumbnails,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +50,56,otw_use_impeller,switch to impeller data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, +51,57,otw_use_gps,switch to GPS data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, +52,58,workout_toggle_ranking,toggle ranking,TRUE,302,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, +53,59,team_workout_upload_view,upload workout for team member,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +54,60,workout_upload_view,upload a workout,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +55,61,workout_histo_view,histogram ,TRUE,302,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +57,63,workout_unsubscribe_view,unsubscribe from comments,TRUE,302,basic,200,302,basic,200,302,basic,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +58,64,workout_comment_view,comment on workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +59,65,workout_tcxemail_view,download TCX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +60,66,workout_gpxemail_view,download GPX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +61,67,workout_csvemail_view,download CSV file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +62,68,workout_csvtoadmin_view,send CSV to admin,TRUE,403,basic,200,200,basic,200,200,coach,200,200,TRUE,FALSE,TRUE,FALSE,TRUE, +63,69,workout_edit_view,Edit Workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +64,70,workout_map_view,View workout Map,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +65,71,workout_update_cp_view,Update CP data based on new workout,TRUE,403,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, +66,72,instroke_chart,View In-Stroke data chart,TRUE,302,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,FALSE,FALSE,FALSE, +67,73,instroke_view,Create in stroke chart,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +68,74,workout_stats_view,View Workout Stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +69,75,workout_data_view,shows the detailed data for a workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +70,76,workout_otwsetpower_view,set weight and start offline OTW power calculations,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +71,77,workout_otwpowerplot_view,generates OTW power chart,TRUE,200,basic,200,200,basic,200,200,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +72,78,workout_wind_view,set wind,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +73,79,workout_uploadimage_view,upload image,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +74,80,virtualevent_compare_view,compare workouts from a virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +75,81,virtualevent_uploadimage_view,upload image to virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +76,82,virtualevent_setlogo_view,set logo of virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +77,83,logo_delete_view,delete logo,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +78,84,workout_downloadwind_view,download Wind,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,FALSE, +79,85,workout_downloadmetar_view,download METAR,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,FALSE, +80,86,workout_stream_view,edit stream (redirects as no data in test suite),TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +81,87,workout_summary_edit_view,edit workout summary,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +82,88,workout_summary_restore_view,restore workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE, +83,89,workout_split_view,split workout,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +84,90,workout_view,view workout ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +85,91,workout_video_create_view,add video ,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +86,92,VideoDelete,delete video,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +87,93,workout_video_view_mini,minimal view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +88,94,workout_video_view,"video view (not tested, no video in test suite)",TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +89,95,list_videos,list my videos,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +90,96,video_selectworkout,select workout to create video,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +91,98,workout_fusion_view,needs to workouts,TRUE,403,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +92,99,workout_fusion_list,list workouts to fuse with,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +94,103,workout_add_chart_view,add chart,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,FALSE, +95,112,WorkoutDelete,delete workout,TRUE,403,basic,200,302,basic,403,403,coach,403,403,FALSE,FALSE,TRUE,FALSE,TRUE, +96,113,workout_smoothenpace_view,smoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE, +97,114,workout_undo_smoothenpace_view,unsmoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE, +98,115,workout_c2import_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +99,120,workout_stravaimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +100,122,workout_getc2workout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, +101,124,workout_getimportview,imports a workout from third party,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +102,125,workout_getstravaworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, +103,126,workout_getstravaworkout_next,gets all strava workouts,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +104,127,workout_sporttracksimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +105,129,workout_getsporttracksworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, +106,130,workout_polarimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +107,132,workout_runkeeperimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +108,134,workout_underarmourimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +109,135,workout_c2_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +110,136,workout_strava_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +111,137,workout_recalcsummary_view,recalculates workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE, +112,138,workout_sporttracks_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +113,139,workout_runkeeper_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +114,140,workout_underarmour_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +115,141,workout_tp_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +116,142,multi_compare_view,compare workouts ,TRUE,302,basic,302,403,basic,302,403,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, +117,145,alerts_view,view alerts,TRUE,302,basic,200,403,basic,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +118,147,AlertDelete,delete alert,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +119,148,alert_edit_view,edit alert,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +120,150,alert_create_view,create alert,TRUE,302,pro,200,403,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +121,152,alert_report_view,view alerts report,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE, +122,155,boxplot_view,View boxplot,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +123,157,boxplot_view_data,data for boxplot,TRUE,302,pro,200,403,pro,200,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE, +124,158,multiflex_view,view multiflex,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +125,160,multiflex_data,data for multiflex,TRUE,302,pro,200,403,pro,200,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE, +126,161,deactivate_user,deactivate a user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, +127,162,remove_user,remove a user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, +128,163,user_gdpr_confirm,GDPR confirmation,TRUE,302,basic,302,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, +129,165,user_gdpr_optin,GDPR optin,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +130,167,rower_teams_view,view team,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +131,168,rower_calcdps_view,calculates distance per stroke,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +132,169,rower_exportsettings_view,Export settings,TRUE,404,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +133,171,team_view,Team view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +134,172,team_members_stats_view,Stats for team members,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +135,173,team_edit_view,View Team,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +136,174,team_leaveconfirm_view,Leave Team,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +137,175,team_leave_view,Leave Team,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +138,176,team_deleteconfirm_view,Delete Team,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +139,177,team_requestmembership_view,Request team membership,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +140,178,reject_revoke_coach_request,Reject request,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +141,179,coach_drop_athlete_confirm_view,Drop athlete,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +142,180,coach_drop_athlete_view,Drop athlete,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +143,181,athlete_drop_coach_confirm_view,Drop athlete,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +144,182,athlete_drop_coach_view,Drop coach,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +145,184,reject_revoke_coach_offer,Reject offer,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +146,186,request_coaching_view,Request coaching,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +147,187,offer_coaching_view,Offer Coaching,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +148,188,coach_accept_coachrequest_view,Accept Coach request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +149,189,rower_accept_coachoffer_view,Accept Coach offer,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +150,190,team_delete_view,Delete Team,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +151,191,team_create_view,Create Team; this test redirects because pro user already has a team,TRUE,302,pro,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +152,192,manager_member_drop_view,Drop member,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +153,193,invitation_reject_view,Reject invitation,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +154,194,invitation_revoke_view,redirects when no invitations,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +155,195,rower_invitations_view,See Invitations,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +156,196,raise_500,Test 500 error,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +157,198,request_revoke_view,redirects when no invitations,TRUE,302,basic,302,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +158,199,request_reject_view,rejects request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +159,200,manager_requests_view,views requests,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +160,202,rower_edit_view,edit rower preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +161,204,rower_prefs_view,edit more preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, diff --git a/rowers/urls.py b/rowers/urls.py index 7735e0b6..4fb084e1 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -461,7 +461,7 @@ urlpatterns = [ re_path(r'^me/teams/$',views.rower_teams_view,name='rower_teams_view'), re_path(r'^me/calcdps/$',views.rower_calcdps_view,name='rower_calcdps_view'), re_path(r'^me/exportsettings/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), - re_path(r'^me/exportsettings/user/(?P\d+)/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), + re_path(r'^me/exportsettings/user/(?P\d+)/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), re_path(r'^team/(?P\d+)/$',views.team_view,name='team_view'), re_path(r'^team/(?P\d+)/memberstats/$',views.team_members_stats_view,name='team_members_stats_view'), re_path(r'^team/(?P\d+)/edit/$',views.team_edit_view,name='team_edit_view'), @@ -506,10 +506,10 @@ urlpatterns = [ re_path(r'^me/request/(\w+.*)/$',views.manager_requests_view,name='manager_requests_view'), re_path(r'^me/request/$',views.manager_requests_view,name='manager_requests_view'), re_path(r'^me/edit/$',views.rower_edit_view,name='rower_edit_view'), - re_path(r'^me/edit/user/(?P\d+)/$',views.rower_edit_view,name='rower_edit_view'), + re_path(r'^me/edit/user/(?P\d+)/$',views.rower_edit_view,name='rower_edit_view'), re_path(r'^me/preferences/$',views.rower_prefs_view,name='rower_prefs_view'), re_path(r'^me/transactions/$',views.transactions_view,name='transactions_view'), - re_path(r'^me/preferences/user/(?P\d+)/$',views.rower_prefs_view,name='rower_prefs_view'), + re_path(r'^me/preferences/user/(?P\d+)/$',views.rower_prefs_view,name='rower_prefs_view'), re_path(r'^me/edit/(.+.*)/$',views.rower_edit_view,name='rower_edit_view'), re_path(r'^me/c2authorize/$',views.rower_c2_authorize,name='rower_c2_authorize'), re_path(r'^me/polarauthorize/$',views.rower_polar_authorize,name='rower_polar_authorize'), diff --git a/rowers/views/otherviews.py b/rowers/views/otherviews.py index 1e9572ba..f0813448 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,120 +74,6 @@ 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() @@ -200,7 +86,7 @@ def analysis_view(request,userid=0): 'rower':r, } ) - + # Shows laboratory page @login_required() def laboratory_view(request,userid=0): @@ -212,9 +98,3 @@ def laboratory_view(request,userid=0): 'rower':r, } ) - - - - - - diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index a97c1ce6..7fda180c 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -12,7 +12,7 @@ def get_team_by_pk(request,team_id): -#@login_required() +@login_required() @permission_required('teams.view_team',fn=get_team_by_pk,raise_exception=True) def team_view(request,team_id=0,userid=0): ismember = 0 @@ -587,6 +587,7 @@ def rower_invitations_view(request,code=None,message='',successmessage=''): }) return HttpResponseRedirect(url) +@login_required() @permission_required('teams.change_team',fn=get_team_by_pk,raise_exception=True) def team_edit_view(request, team_id=0): t = get_object_or_404(Team,pk=team_id) @@ -649,6 +650,7 @@ def team_edit_view(request, team_id=0): 'team':t, }) +@login_required() @user_passes_test(can_add_team,login_url="/rowers/paidplans",redirect_field_name=None) def team_create_view(request): r = getrequestrower(request) @@ -732,6 +734,7 @@ def team_deleteconfirm_view(request,team_id): 'active':'nav-teams', }) +@login_required() @permission_required('teams.delete_team',fn=get_team_by_pk,raise_exception=True) def team_delete_view(request,team_id): r = getrower(request.user) @@ -743,6 +746,7 @@ def team_delete_view(request,team_id): response = HttpResponseRedirect(url) return response +@login_required() @permission_required('teams.change_team',fn=get_team_by_pk,raise_exception=True) def team_members_stats_view(request,team_id): r = getrower(request.user) diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index d82f5b47..735fd5df 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -5,6 +5,120 @@ from __future__ import unicode_literals from rowers.views.statements import * +@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 + + @login_required() def survey(request): @@ -167,9 +281,10 @@ def rower_favoritecharts_view(request,id=0): return render(request,'favoritecharts.html',context) # page where user sets his export settings -@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) -def rower_exportsettings_view(request,id=0): - r = getrequestrower(request,userid=id) +@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': form = RowerExportForm(request.POST) if form.is_valid(): @@ -203,9 +318,10 @@ def rower_exportsettings_view(request,id=0): # Page where user can set his details # Add email address to form so user can change his email address -@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) -def rower_edit_view(request,rowerid=0,id=0,message=""): - r = getrequestrower(request,rowerid=rowerid,userid=id,notpermanent=True) +@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) rowerid = r.id @@ -298,9 +414,10 @@ def rower_edit_view(request,rowerid=0,id=0,message=""): # Page where user can set his details # Add email address to form so user can change his email address -@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) -def rower_prefs_view(request,id=0,message=""): - r = getrequestrower(request,userid=id,notpermanent=True) +@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) rowerid = r.id From 4fb22ebc7d670d4e29e24583cbcdf82516c5c55e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 5 Feb 2020 22:57:18 +0100 Subject: [PATCH 75/80] not correct --- rowers/tests/test_permissions2.py | 2 +- rowers/tests/viewnames.csv | 97 +++++++++++++++---------------- rowers/urls.py | 2 +- rowers/views/userviews.py | 1 + rowers/views/workoutviews.py | 2 +- 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index 4fc84ad7..8d193337 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -369,7 +369,7 @@ class PermissionsViewTests(TestCase): print(view,'member') print() - if permissions['member'] in plans and not permissions['is_staff'] and permissions['dotest']: + if permissions['member'] in plans and permissions['is_staff'] is False and permissions['dotest']: urlstotest = [] falseurlstotest = [] otheruserurls = [] diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 62375b71..e20afca6 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -149,7 +149,7 @@ 148,188,coach_accept_coachrequest_view,Accept Coach request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 149,189,rower_accept_coachoffer_view,Accept Coach offer,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 150,190,team_delete_view,Delete Team,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -151,191,team_create_view,Create Team; this test redirects because pro user already has a team,TRUE,302,pro,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +151,191,team_create_view,Create Team,TRUE,302,pro,302,302,FALSE,200,200,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, 152,192,manager_member_drop_view,Drop member,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 153,193,invitation_reject_view,Reject invitation,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 154,194,invitation_revoke_view,redirects when no invitations,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, @@ -160,54 +160,53 @@ 159,200,manager_requests_view,views requests,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 160,202,rower_edit_view,edit rower preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 161,204,rower_prefs_view,edit more preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, -162,205,transactions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -163,208,rower_c2_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -164,209,rower_polar_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -165,210,rower_revokeapp_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -166,211,rower_strava_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -167,212,rower_sporttracks_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -168,213,rower_underarmour_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -169,214,rower_tp_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -170,215,rower_runkeeper_authorize,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -171,216,rower_sporttracks_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -172,217,rower_underarmour_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -173,218,rower_tp_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -174,219,rower_c2_token_refresh,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -175,220,rower_favoritecharts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -176,222,workout_workflow_config2_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -177,224,workflow_default_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -178,225,sendmail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -179,231,analysis_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -180,232,laboratory_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -181,233,errormessage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -182,237,payment_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -183,238,upgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -184,240,downgrade_confirm_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -185,241,billing_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -186,242,upgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -187,243,downgrade_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -188,244,payment_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -189,245,downgrade_completed_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -190,246,paidplans_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -191,247,plan_stop_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -192,248,plan_tobasic_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, -193,249,checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -194,250,upgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -195,251,downgrade_checkouts_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -196,252,planrequired_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -197,253,start_trial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -198,254,start_plantrial_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -199,256,rower_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -200,257,freecoach_register_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -201,259,workout_workflow_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, -202,260,workout_flexchart3_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, -203,264,rower_process_testcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -204,265,rower_create_trainingplan,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -205,267,TrainingPlanDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -206,268,MicroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -207,269,MesoCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -208,270,MacroCycleDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -209,271,rower_trainingplan_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +162,205,transactions_view,view transactions (staff only),TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +163,208,rower_c2_authorize,redirects to C2 auth page,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +164,209,rower_polar_authorize,redirects to Polar,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +165,210,rower_revokeapp_view,revoke app,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +166,211,rower_strava_authorize,authorize on strava,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +167,212,rower_sporttracks_authorize,authorize on sporttracks,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +168,213,rower_underarmour_authorize,authorize on UA,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +169,214,rower_tp_authorize,authorize on TP,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +170,215,rower_runkeeper_authorize,authorize on RK,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +171,216,rower_sporttracks_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +172,217,rower_underarmour_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +173,218,rower_tp_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +174,219,rower_c2_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +175,220,rower_favoritecharts_view,See favorite charts,TRUE,302,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +176,222,workout_workflow_config2_view,configure workflow,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,FALSE, +177,224,workflow_default_view,resets workflow to default,TRUE,302,basic,302,302,FALSE,403,403,FALSE,302,403,FALSE,FALSE,FALSE,TRUE,TRUE, +178,225,sendmail,feedback form,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +180,232,laboratory_view,lab,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +181,233,errormessage_view,not used,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +182,237,payment_confirm_view,confirm payment,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +183,238,upgrade_confirm_view,confirm upgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +184,240,downgrade_confirm_view,confirm downgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +185,241,billing_view,confirm billing,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +186,242,upgrade_view,confirm upgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +187,243,downgrade_view,confirm downgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +188,244,payment_completed_view,payment completed,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +189,245,downgrade_completed_view,downgrade completed,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +190,246,paidplans_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +191,247,plan_stop_view,stop plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +192,248,plan_tobasic_view,reset to basic,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +193,249,checkouts_view,checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +194,250,upgrade_checkouts_view,upgrade checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +195,251,downgrade_checkouts_view,downgrade checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +196,252,planrequired_view,a plan is required,TRUE,302,basic,302,302,basic,302,302,coach,302,302,FALSE,FALSE,FALSE,TRUE,TRUE, +197,253,start_trial_view,payments,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +198,254,start_plantrial_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +199,256,rower_register_view,register rower,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +200,257,freecoach_register_view,register coach,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +201,259,workout_workflow_view,workout workflow vieq,TRUE,200,basic,200,200,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,TRUE,TRUE, +202,260,workout_flexchart3_view,flex chart,TRUE,200,basic,200,403,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,TRUE,TRUE, +203,264,rower_process_testcallback,test callback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +204,265,rower_create_trainingplan,create training plan,TRUE,302,plan,200,302,FALSE,403,403,FALSE,200,403,FALSE,FALSE,FALSE,TRUE,TRUE, +205,267,TrainingPlanDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +206,268,MicroCycleDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +207,269,MesoCycleDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +208,270,MacroCycleDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +209,271,rower_trainingplan_view,see training plan,TRUE,200,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, diff --git a/rowers/urls.py b/rowers/urls.py index 4fb084e1..ca7c045d 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -539,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/$', views.laboratory_view,name='laboratory'), re_path(r'^laboratory/user/(?P\d+)/$', views.laboratory_view,name='laboratory'), 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'), diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index 735fd5df..9d39feef 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -214,6 +214,7 @@ def start_plantrial_view(request): return HttpResponseRedirect(url) # Page where user can manage his favorite charts +@login_required() @permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) def rower_favoritecharts_view(request,id=0): message = '' diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index a52d31e4..587974e5 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -3283,7 +3283,7 @@ def workout_workflow_config2_view(request,userid=0): # Workflow View -@login_required() +#@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'] From 2d97e610f63e5fa56f37bd3f5fc9f2815cb650c7 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 6 Feb 2020 17:23:18 +0100 Subject: [PATCH 76/80] passing a few more tests --- rowers/forms.py | 1 + rowers/tests/viewnames.csv | 88 +++++++++++++++++------------------ rowers/urls.py | 12 ++--- rowers/views/analysisviews.py | 2 +- rowers/views/otherviews.py | 2 + rowers/views/planviews.py | 1 + rowers/views/statements.py | 2 + rowers/views/teamviews.py | 4 +- rowers/views/userviews.py | 6 +-- rowers/views/workoutviews.py | 7 +-- 10 files changed, 66 insertions(+), 59 deletions(-) diff --git a/rowers/forms.py b/rowers/forms.py index 20882308..530c842d 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -1404,6 +1404,7 @@ class FlexAxesForm(forms.Form): extrametrics = kwargs.pop('extrametrics',[]) super(FlexAxesForm, self).__init__(*args, **kwargs) + rower = Rower.objects.get(user=request.user) axchoicespro = ( diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index e20afca6..50f70e99 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -149,18 +149,18 @@ 148,188,coach_accept_coachrequest_view,Accept Coach request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 149,189,rower_accept_coachoffer_view,Accept Coach offer,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 150,190,team_delete_view,Delete Team,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -151,191,team_create_view,Create Team,TRUE,302,pro,302,302,FALSE,200,200,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +151,191,team_create_view,Create Team,TRUE,302,pro,302,302,FALSE,200,200,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, 152,192,manager_member_drop_view,Drop member,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 153,193,invitation_reject_view,Reject invitation,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 154,194,invitation_revoke_view,redirects when no invitations,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -155,195,rower_invitations_view,See Invitations,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +155,195,rower_invitations_view,See Invitations,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, 156,196,raise_500,Test 500 error,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 157,198,request_revoke_view,redirects when no invitations,TRUE,302,basic,302,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 158,199,request_reject_view,rejects request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 159,200,manager_requests_view,views requests,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -160,202,rower_edit_view,edit rower preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, -161,204,rower_prefs_view,edit more preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, -162,205,transactions_view,view transactions (staff only),TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +160,202,rower_edit_view,edit rower preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +161,204,rower_prefs_view,edit more preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +162,205,transactions_view,view transactions (staff only),TRUE,302,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE,TRUE, 163,208,rower_c2_authorize,redirects to C2 auth page,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 164,209,rower_polar_authorize,redirects to Polar,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 165,210,rower_revokeapp_view,revoke app,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, @@ -173,11 +173,11 @@ 172,217,rower_underarmour_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 173,218,rower_tp_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 174,219,rower_c2_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -175,220,rower_favoritecharts_view,See favorite charts,TRUE,302,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +175,220,rower_favoritecharts_view,See favorite charts,TRUE,302,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, 176,222,workout_workflow_config2_view,configure workflow,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,FALSE, -177,224,workflow_default_view,resets workflow to default,TRUE,302,basic,302,302,FALSE,403,403,FALSE,302,403,FALSE,FALSE,FALSE,TRUE,TRUE, -178,225,sendmail,feedback form,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, -180,232,laboratory_view,lab,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +177,224,workflow_default_view,resets workflow to default,TRUE,302,basic,302,302,FALSE,403,403,FALSE,302,403,FALSE,FALSE,FALSE,FALSE,TRUE, +178,225,sendmail,feedback form,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +180,232,laboratory_view,lab,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, 181,233,errormessage_view,not used,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 182,237,payment_confirm_view,confirm payment,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 183,238,upgrade_confirm_view,confirm upgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, @@ -187,53 +187,53 @@ 187,243,downgrade_view,confirm downgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 188,244,payment_completed_view,payment completed,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 189,245,downgrade_completed_view,downgrade completed,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -190,246,paidplans_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +190,246,paidplans_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, 191,247,plan_stop_view,stop plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 192,248,plan_tobasic_view,reset to basic,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 193,249,checkouts_view,checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 194,250,upgrade_checkouts_view,upgrade checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 195,251,downgrade_checkouts_view,downgrade checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -196,252,planrequired_view,a plan is required,TRUE,302,basic,302,302,basic,302,302,coach,302,302,FALSE,FALSE,FALSE,TRUE,TRUE, +196,252,planrequired_view,a plan is required,TRUE,302,basic,302,302,basic,302,302,coach,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, 197,253,start_trial_view,payments,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 198,254,start_plantrial_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -199,256,rower_register_view,register rower,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, -200,257,freecoach_register_view,register coach,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, -201,259,workout_workflow_view,workout workflow vieq,TRUE,200,basic,200,200,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,TRUE,TRUE, -202,260,workout_flexchart3_view,flex chart,TRUE,200,basic,200,403,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,TRUE,TRUE, +199,256,rower_register_view,register rower,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +200,257,freecoach_register_view,register coach,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +201,259,workout_workflow_view,workout workflow vieq,TRUE,302,basic,200,200,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,FALSE,TRUE, +202,260,workout_flexchart3_view,flex chart,TRUE,302,basic,200,403,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,FALSE,TRUE, 203,264,rower_process_testcallback,test callback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -204,265,rower_create_trainingplan,create training plan,TRUE,302,plan,200,302,FALSE,403,403,FALSE,200,403,FALSE,FALSE,FALSE,TRUE,TRUE, +204,265,rower_create_trainingplan,create training plan,TRUE,302,plan,200,302,FALSE,403,403,FALSE,200,403,FALSE,FALSE,FALSE,FALSE,TRUE, 205,267,TrainingPlanDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 206,268,MicroCycleDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 207,269,MesoCycleDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 208,270,MacroCycleDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -209,271,rower_trainingplan_view,see training plan,TRUE,200,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, -210,279,rower_trainingplan_execution_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -211,281,TrainingMacroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -212,282,TrainingMesoCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -213,283,planmacrocyclebymonth,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -214,285,planmesocyclebyweek,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -215,287,TrainingMicroCycleUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -216,288,rower_delete_trainingtarget,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -217,289,TrainingPlanUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -218,290,TrainingTargetUpdate,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -219,291,strokedataform,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -220,292,plannedsession_teamcreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -221,296,plannedsession_teamedit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -222,298,plannedsession_create_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -223,300,plannedsession_multiclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -224,302,plannedsession_multicreate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -225,305,plannedsession_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -226,306,plannedsession_totemplate_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -227,307,plannedsession_compare_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -228,310,plannedsession_clone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -229,311,plannedsession_teamclone_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -230,313,plannedsession_detach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -231,315,plannedsession_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -232,317,PlannedSessionDelete,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -233,319,plannedsessions_manage_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -234,323,plannedsessions_coach_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -235,325,plannedsessions_coach_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -236,326,plannedsessions_print_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +209,271,rower_trainingplan_view,see training plan,TRUE,200,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,FALSE, +210,279,rower_trainingplan_execution_view,see training plan execution,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +211,281,TrainingMacroCycleUpdate,update macro cycle,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +212,282,TrainingMesoCycleUpdate,update cycle,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +213,283,planmacrocyclebymonth,update cycle,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +214,285,planmesocyclebyweek,plan meso by week,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +215,287,TrainingMicroCycleUpdate,update cycle,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +216,288,rower_delete_trainingtarget,delete training target,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +217,289,TrainingPlanUpdate,update trainingplan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +218,290,TrainingTargetUpdate,update training target,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +219,291,strokedataform,test strokedata,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +220,292,plannedsession_teamcreate_view,create planned session for team,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +221,296,plannedsession_teamedit_view,edit planned sesssion,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +222,298,plannedsession_create_view,create planned session ,TRUE,302,plan,200,302,FALSE,200,302,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +223,300,plannedsession_multiclone_view,clone multiple planned sessions,TRUE,302,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +224,302,plannedsession_multicreate_view,create multiple planned sessions,TRUE,302,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +225,305,plannedsession_edit_view,edit planned sesssion,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +226,306,plannedsession_totemplate_view,planned session to template,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +227,307,plannedsession_compare_view,compare workouts from planned session,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +228,310,plannedsession_clone_view,clone planned session,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +229,311,plannedsession_teamclone_view,clone team planned session,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +230,313,plannedsession_detach_view,remove workout from session,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +231,315,plannedsession_view,view session,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +232,317,PlannedSessionDelete,delete session,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +233,319,plannedsessions_manage_view,manage planned sesions,TRUE,302,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +234,323,plannedsessions_coach_view,team view,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +235,325,plannedsessions_coach_icsemail_view,team view,TRUE,302,coach,302,403,coach,302,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, +236,326,plannedsessions_print_view,print view,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, 239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, diff --git a/rowers/urls.py b/rowers/urls.py index ca7c045d..00b83ac9 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -524,7 +524,7 @@ urlpatterns = [ re_path(r'^me/tprefresh/$',views.rower_tp_token_refresh,name='rower_tp_token_refresh'), re_path(r'^me/c2refresh/$',views.rower_c2_token_refresh,name='rower_c2_token_refresh'), re_path(r'^me/favoritecharts/$',views.rower_favoritecharts_view,name='rower_favoritecharts_view'), - re_path(r'^me/favoritecharts/user/(?P\d+)/$',views.rower_favoritecharts_view,name='rower_favoritecharts_view'), + re_path(r'^me/favoritecharts/user/(?P\d+)/$',views.rower_favoritecharts_view,name='rower_favoritecharts_view'), # re_path(r'^me/workflowconfig/$',views.workout_workflow_config_view), re_path(r'^me/workflowconfig2/$',views.workout_workflow_config2_view,name='workout_workflow_config2_view'), re_path(r'^me/workflowconfig2/user/(?P\d+)/$',views.workout_workflow_config2_view,name='workout_workflow_config2_view'), @@ -539,8 +539,8 @@ 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/$', views.laboratory_view,name='laboratory'), - 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'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'), @@ -553,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'), @@ -657,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'), @@ -721,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/views/analysisviews.py b/rowers/views/analysisviews.py index 6d09525e..d493f6b5 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -1330,7 +1330,7 @@ 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(isplanmember,login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", diff --git a/rowers/views/otherviews.py b/rowers/views/otherviews.py index f0813448..aa28d89b 100644 --- a/rowers/views/otherviews.py +++ b/rowers/views/otherviews.py @@ -77,6 +77,7 @@ def errormessage_view(request,errormessage='aap'): # 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, @@ -89,6 +90,7 @@ def analysis_view(request,userid=0): # 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, diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index b5ec6dad..034a1ba9 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1216,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): diff --git a/rowers/views/statements.py b/rowers/views/statements.py index e6a6e7e6..c011da93 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -385,6 +385,8 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): 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 diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 7fda180c..16a8cc60 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -442,7 +442,7 @@ def team_requestmembership_view(request,teamid,userid): messages.error(request, "You have to be on a paid plan (Pro or higher) to join this team. As a basic user you can only join teams managed by users on the Coach plan.") - url = reverse('paidplans') + url = reverse('paidplans_view') return HttpResponseRedirect(url) res,text = teams.create_request(t,userid) @@ -669,7 +669,7 @@ def team_create_view(request): if not res: messages.error(request,message) - url = reverse('paidplans') + url = reverse('paidplans_view') return HttpResponseRedirect(url) url = reverse('rower_teams_view') diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index 9d39feef..c38422ab 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -215,11 +215,11 @@ def start_plantrial_view(request): # Page where user can manage his favorite charts @login_required() -@permission_required('rower.is_coach',fn=get_user_by_id,raise_exception=True) -def rower_favoritecharts_view(request,id=0): +@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) +def rower_favoritecharts_view(request,userid=0): message = '' successmessage = '' - r = getrequestrower(request,userid=id,notpermanent=True) + r = getrequestrower(request,userid=userid,notpermanent=True) favorites = FavoriteChart.objects.filter(user=r).order_by('id') aantal = len(favorites) favorites_data = [{'yparam1':f.yparam1, diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 587974e5..ab64ca8b 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -3283,7 +3283,7 @@ def workout_workflow_config2_view(request,userid=0): # Workflow View -#@login_required() +@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'] @@ -3371,6 +3371,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): @@ -3390,11 +3391,11 @@ 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 = ispromember(request.user) if result: promember=1 @@ -3898,7 +3899,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""): 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) From c87bff43d86a78b63a99d02d86f4d87381d6c2a2 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 6 Feb 2020 21:59:16 +0100 Subject: [PATCH 77/80] fixing errors in permissions tests --- rowers/tests/test_permissions.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index c5576c89..ffaf1658 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -753,7 +753,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_prefs_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) @@ -764,7 +764,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_prefs_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -801,7 +801,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_exportsettings_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_exportsettings_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) @@ -812,7 +812,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_exportsettings_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_exportsettings_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -825,7 +825,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_edit_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_edit_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) @@ -836,7 +836,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_edit_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_edit_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -1077,7 +1077,7 @@ class PermissionsViewTests(TestCase): self.assertTrue(form.is_valid()) - expected_url = reverse('paidplans') + expected_url = reverse('paidplans_view') response = self.c.post(url,form_data,follow=True) self.assertRedirects(response, @@ -1128,7 +1128,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.uplan2.username, password=self.uplan2password) self.assertTrue(login) - url = reverse('rower_prefs_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -1214,7 +1214,7 @@ class PermissionsViewTests(TestCase): self.assertTrue(form.is_valid()) - expected_url = reverse('paidplans') + expected_url = reverse('paidplans_view') response = self.c.post(url,form_data,follow=True) self.assertRedirects(response, @@ -1236,7 +1236,7 @@ class PermissionsViewTests(TestCase): response = self.c.get(url,follow=True) self.assertEqual(response.status_code,200) - expected_url = reverse('paidplans') + expected_url = reverse('paidplans_view') self.assertRedirects(response, expected_url=expected_url, @@ -1250,7 +1250,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.upro2.username, password=self.upro2password) self.assertTrue(login) - url = reverse('rower_prefs_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) @@ -1289,7 +1289,7 @@ class PermissionsViewTests(TestCase): response = self.c.get(url,follow=True) self.assertEqual(response.status_code,200) - expected_url = reverse('paidplans') + expected_url = reverse('paidplans_view') self.assertRedirects(response, expected_url = expected_url, status_code=302,target_status_code=200) @@ -1421,7 +1421,7 @@ class PermissionsViewTests(TestCase): response = self.c.get(url,follow=True) self.assertEqual(response.status_code,200) - expected_url = reverse('paidplans') + expected_url = reverse('paidplans_view') self.assertRedirects(response, expected_url = expected_url, status_code=302,target_status_code=200) From 3c7a89f822056b45c59b1c025ec7532c9007a8c7 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 7 Feb 2020 17:03:47 +0100 Subject: [PATCH 78/80] done all views --- rowers/tests/viewnames.csv | 360 ++++++++++++++++++------------------- 1 file changed, 180 insertions(+), 180 deletions(-) diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 50f70e99..bec2388f 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -1,14 +1,14 @@ ,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest,realtest,kwargs -0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, -1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,TRUE,TRUE, +1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, 2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -4,5,fitness_metric_view,updates a person's fitness metric,TRUE,302,basic,302,302,FALSE,403,302,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, -5,6,agegrouprecordview,shows ergo age group records,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, -7,8,virtualevents_view,virtual races list,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -8,9,virtualevent_create_view,create virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -9,10,indoorvirtualevent_create_view,create indoor virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +4,5,fitness_metric_view,updates a person's fitness metric,TRUE,302,basic,302,302,FALSE,403,302,FALSE,403,403,FALSE,FALSE,FALSE,TRUE,TRUE, +5,6,agegrouprecordview,shows ergo age group records,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +7,8,virtualevents_view,virtual races list,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +8,9,virtualevent_create_view,create virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +9,10,indoorvirtualevent_create_view,create indoor virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, 10,11,virtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 11,12,indoorvirtualevent_toggle_email_view,toggle email notifications,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 12,13,virtualevent_view,view virtual race,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, @@ -21,116 +21,116 @@ 19,20,virtualevent_withdraw_view,withdraw,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 20,21,virtualevent_submit_result_view,submit a result,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, 21,22,virtualevent_disqualify_view,disqualify a racer,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -22,24,courses_view,see race courses,TRUE,200,FALSE,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -23,25,course_upload_view,upload a new course,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -24,26,addmanual_view,upload a manual workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -25,27,team_comparison_select,compare with a team member workout,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,TRUE,TRUE,FALSE,TRUE, -26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, -27,29,workouts_join_select,select workouts to join,TRUE,404,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, -28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, -29,31,analysis_new,analysis front page,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, -30,32,user_multiflex_select,select multiflex data,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, -31,33,session_jobs_view,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -32,34,session_jobs_status,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +22,24,courses_view,see race courses,TRUE,200,FALSE,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +23,25,course_upload_view,upload a new course,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +24,26,addmanual_view,upload a manual workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +25,27,team_comparison_select,compare with a team member workout,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,TRUE,TRUE,TRUE,TRUE, +26,28,workouts_join_view,join workouts,TRUE,302,pro,302,302,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, +27,29,workouts_join_select,select workouts to join,TRUE,404,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +28,30,user_boxplot_select,select boxplots,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +29,31,analysis_new,analysis front page,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +30,32,user_multiflex_select,select multiflex data,TRUE,302,pro,200,302,FALSE,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +31,33,session_jobs_view,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +32,34,session_jobs_status,view jobs,TRUE,302,basic,200,302,FALSE,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, 33,35,kill_async_job,kill job,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 34,36,post_progress,post progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -35,37,graphs_view,view charts,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, -36,38,fitnessmetric_view,view fitness metric,TRUE,302,plan,200,302,plan,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, -37,39,rankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, -38,40,rankings_view2,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, -39,41,otwrankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, -40,42,oterankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +35,37,graphs_view,view charts,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +36,38,fitnessmetric_view,view fitness metric,TRUE,302,plan,200,302,plan,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +37,39,rankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +38,40,rankings_view2,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +39,41,otwrankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +40,42,oterankings_view,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, 41,43,cum_flex,flex all chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -42,44,analysis_view_data,redirects to analysis direct,TRUE,302,pro,302,302,pro,302,302,coach,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, +42,44,analysis_view_data,redirects to analysis direct,TRUE,302,pro,302,302,pro,302,302,coach,302,302,FALSE,FALSE,FALSE,TRUE,TRUE, 43,47,cum_flex_data,flex all chart data (json),TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -44,48,histo,histogram view,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, -45,49,histo_data,histogram data,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -46,51,cumstats,cumulative statistics,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +44,48,histo,histogram view,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, +45,49,histo_data,histogram data,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +46,51,cumstats,cumulative statistics,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, 47,53,graph_show_view,show a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 48,54,GraphDelete,delete a chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -49,55,get_thumbnails,get thumbnails,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -50,56,otw_use_impeller,switch to impeller data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, -51,57,otw_use_gps,switch to GPS data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, -52,58,workout_toggle_ranking,toggle ranking,TRUE,302,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, -53,59,team_workout_upload_view,upload workout for team member,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -54,60,workout_upload_view,upload a workout,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -55,61,workout_histo_view,histogram ,TRUE,302,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -57,63,workout_unsubscribe_view,unsubscribe from comments,TRUE,302,basic,200,302,basic,200,302,basic,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -58,64,workout_comment_view,comment on workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -59,65,workout_tcxemail_view,download TCX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, -60,66,workout_gpxemail_view,download GPX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, -61,67,workout_csvemail_view,download CSV file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, -62,68,workout_csvtoadmin_view,send CSV to admin,TRUE,403,basic,200,200,basic,200,200,coach,200,200,TRUE,FALSE,TRUE,FALSE,TRUE, -63,69,workout_edit_view,Edit Workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, -64,70,workout_map_view,View workout Map,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -65,71,workout_update_cp_view,Update CP data based on new workout,TRUE,403,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, +49,55,get_thumbnails,get thumbnails,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +50,56,otw_use_impeller,switch to impeller data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE,TRUE, +51,57,otw_use_gps,switch to GPS data,TRUE,403,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE,TRUE, +52,58,workout_toggle_ranking,toggle ranking,TRUE,302,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE,TRUE, +53,59,team_workout_upload_view,upload workout for team member,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +54,60,workout_upload_view,upload a workout,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +55,61,workout_histo_view,histogram ,TRUE,302,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +57,63,workout_unsubscribe_view,unsubscribe from comments,TRUE,302,basic,200,302,basic,200,302,basic,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +58,64,workout_comment_view,comment on workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +59,65,workout_tcxemail_view,download TCX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE, +60,66,workout_gpxemail_view,download GPX file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE, +61,67,workout_csvemail_view,download CSV file,TRUE,403,basic,200,302,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE, +62,68,workout_csvtoadmin_view,send CSV to admin,TRUE,403,basic,200,200,basic,200,200,coach,200,200,TRUE,FALSE,TRUE,TRUE,TRUE, +63,69,workout_edit_view,Edit Workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE, +64,70,workout_map_view,View workout Map,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +65,71,workout_update_cp_view,Update CP data based on new workout,TRUE,403,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,TRUE,TRUE,TRUE, 66,72,instroke_chart,View In-Stroke data chart,TRUE,302,pro,302,302,pro,403,403,coach,302,302,FALSE,FALSE,FALSE,FALSE,FALSE, -67,73,instroke_view,Create in stroke chart,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -68,74,workout_stats_view,View Workout Stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -69,75,workout_data_view,shows the detailed data for a workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, -70,76,workout_otwsetpower_view,set weight and start offline OTW power calculations,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, -71,77,workout_otwpowerplot_view,generates OTW power chart,TRUE,200,basic,200,200,basic,200,200,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -72,78,workout_wind_view,set wind,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, -73,79,workout_uploadimage_view,upload image,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, +67,73,instroke_view,Create in stroke chart,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +68,74,workout_stats_view,View Workout Stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +69,75,workout_data_view,shows the detailed data for a workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE, +70,76,workout_otwsetpower_view,set weight and start offline OTW power calculations,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE, +71,77,workout_otwpowerplot_view,generates OTW power chart,TRUE,200,basic,200,200,basic,200,200,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +72,78,workout_wind_view,set wind,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE, +73,79,workout_uploadimage_view,upload image,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE, 74,80,virtualevent_compare_view,compare workouts from a virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 75,81,virtualevent_uploadimage_view,upload image to virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 76,82,virtualevent_setlogo_view,set logo of virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 77,83,logo_delete_view,delete logo,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 78,84,workout_downloadwind_view,download Wind,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,FALSE, 79,85,workout_downloadmetar_view,download METAR,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,FALSE, -80,86,workout_stream_view,edit stream (redirects as no data in test suite),TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -81,87,workout_summary_edit_view,edit workout summary,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,TRUE, -82,88,workout_summary_restore_view,restore workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE, -83,89,workout_split_view,split workout,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -84,90,workout_view,view workout ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, -85,91,workout_video_create_view,add video ,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +80,86,workout_stream_view,edit stream (redirects as no data in test suite),TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +81,87,workout_summary_edit_view,edit workout summary,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE, +82,88,workout_summary_restore_view,restore workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,TRUE, +83,89,workout_split_view,split workout,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +84,90,workout_view,view workout ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, +85,91,workout_video_create_view,add video ,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, 86,92,VideoDelete,delete video,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 87,93,workout_video_view_mini,minimal view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, 88,94,workout_video_view,"video view (not tested, no video in test suite)",TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, -89,95,list_videos,list my videos,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -90,96,video_selectworkout,select workout to create video,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +89,95,list_videos,list my videos,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +90,96,video_selectworkout,select workout to create video,TRUE,302,pro,200,302,pro,403,403,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, 91,98,workout_fusion_view,needs to workouts,TRUE,403,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -92,99,workout_fusion_list,list workouts to fuse with,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,TRUE, +92,99,workout_fusion_list,list workouts to fuse with,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE, 94,103,workout_add_chart_view,add chart,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,FALSE, -95,112,WorkoutDelete,delete workout,TRUE,403,basic,200,302,basic,403,403,coach,403,403,FALSE,FALSE,TRUE,FALSE,TRUE, -96,113,workout_smoothenpace_view,smoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE, -97,114,workout_undo_smoothenpace_view,unsmoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE, -98,115,workout_c2import_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, -99,120,workout_stravaimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, -100,122,workout_getc2workout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, +95,112,WorkoutDelete,delete workout,TRUE,403,basic,200,302,basic,403,403,coach,403,403,FALSE,FALSE,TRUE,FALSE,FALSE, +96,113,workout_smoothenpace_view,smoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,TRUE, +97,114,workout_undo_smoothenpace_view,unsmoothen pace,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,TRUE, +98,115,workout_c2import_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, +99,120,workout_stravaimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, +100,122,workout_getc2workout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,TRUE,TRUE, 101,124,workout_getimportview,imports a workout from third party,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -102,125,workout_getstravaworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, +102,125,workout_getstravaworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,TRUE,TRUE, 103,126,workout_getstravaworkout_next,gets all strava workouts,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -104,127,workout_sporttracksimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, -105,129,workout_getsporttracksworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, -106,130,workout_polarimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, -107,132,workout_runkeeperimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, -108,134,workout_underarmourimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +104,127,workout_sporttracksimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, +105,129,workout_getsporttracksworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,TRUE,TRUE, +106,130,workout_polarimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, +107,132,workout_runkeeperimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, +108,134,workout_underarmourimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, 109,135,workout_c2_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, 110,136,workout_strava_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, -111,137,workout_recalcsummary_view,recalculates workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,TRUE, +111,137,workout_recalcsummary_view,recalculates workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,TRUE, 112,138,workout_sporttracks_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, 113,139,workout_runkeeper_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, 114,140,workout_underarmour_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, 115,141,workout_tp_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, -116,142,multi_compare_view,compare workouts ,TRUE,302,basic,302,403,basic,302,403,coach,302,302,FALSE,FALSE,TRUE,FALSE,TRUE, -117,145,alerts_view,view alerts,TRUE,302,basic,200,403,basic,403,403,coach,200,302,FALSE,TRUE,FALSE,FALSE,TRUE, +116,142,multi_compare_view,compare workouts ,TRUE,302,basic,302,403,basic,302,403,coach,302,302,FALSE,FALSE,TRUE,TRUE,TRUE, +117,145,alerts_view,view alerts,TRUE,302,basic,200,403,basic,403,403,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE, 118,147,AlertDelete,delete alert,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 119,148,alert_edit_view,edit alert,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -120,150,alert_create_view,create alert,TRUE,302,pro,200,403,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +120,150,alert_create_view,create alert,TRUE,302,pro,200,403,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 121,152,alert_report_view,view alerts report,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE, -122,155,boxplot_view,View boxplot,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +122,155,boxplot_view,View boxplot,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, 123,157,boxplot_view_data,data for boxplot,TRUE,302,pro,200,403,pro,200,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE, -124,158,multiflex_view,view multiflex,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,FALSE,TRUE, +124,158,multiflex_view,view multiflex,TRUE,302,pro,302,403,pro,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, 125,160,multiflex_data,data for multiflex,TRUE,302,pro,200,403,pro,200,403,coach,200,403,FALSE,FALSE,FALSE,FALSE,FALSE, -126,161,deactivate_user,deactivate a user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, -127,162,remove_user,remove a user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, -128,163,user_gdpr_confirm,GDPR confirmation,TRUE,302,basic,302,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,FALSE,TRUE, +126,161,deactivate_user,deactivate a user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,TRUE,TRUE, +127,162,remove_user,remove a user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,TRUE,TRUE, +128,163,user_gdpr_confirm,GDPR confirmation,TRUE,302,basic,302,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,TRUE,TRUE, 129,165,user_gdpr_optin,GDPR optin,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -130,167,rower_teams_view,view team,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -131,168,rower_calcdps_view,calculates distance per stroke,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -132,169,rower_exportsettings_view,Export settings,TRUE,404,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +130,167,rower_teams_view,view team,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +131,168,rower_calcdps_view,calculates distance per stroke,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +132,169,rower_exportsettings_view,Export settings,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 133,171,team_view,Team view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 134,172,team_members_stats_view,Stats for team members,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 135,173,team_edit_view,View Team,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, @@ -149,18 +149,18 @@ 148,188,coach_accept_coachrequest_view,Accept Coach request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 149,189,rower_accept_coachoffer_view,Accept Coach offer,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 150,190,team_delete_view,Delete Team,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -151,191,team_create_view,Create Team,TRUE,302,pro,302,302,FALSE,200,200,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +151,191,team_create_view,Create Team,TRUE,302,pro,302,302,FALSE,200,200,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, 152,192,manager_member_drop_view,Drop member,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 153,193,invitation_reject_view,Reject invitation,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 154,194,invitation_revoke_view,redirects when no invitations,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -155,195,rower_invitations_view,See Invitations,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +155,195,rower_invitations_view,See Invitations,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, 156,196,raise_500,Test 500 error,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 157,198,request_revoke_view,redirects when no invitations,TRUE,302,basic,302,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 158,199,request_reject_view,rejects request,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 159,200,manager_requests_view,views requests,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -160,202,rower_edit_view,edit rower preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, -161,204,rower_prefs_view,edit more preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, -162,205,transactions_view,view transactions (staff only),TRUE,302,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,FALSE,TRUE, +160,202,rower_edit_view,edit rower preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +161,204,rower_prefs_view,edit more preferences,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +162,205,transactions_view,view transactions (staff only),TRUE,302,basic,200,302,basic,200,302,coach,200,302,TRUE,FALSE,FALSE,TRUE,TRUE, 163,208,rower_c2_authorize,redirects to C2 auth page,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 164,209,rower_polar_authorize,redirects to Polar,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 165,210,rower_revokeapp_view,revoke app,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, @@ -173,11 +173,11 @@ 172,217,rower_underarmour_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 173,218,rower_tp_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 174,219,rower_c2_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -175,220,rower_favoritecharts_view,See favorite charts,TRUE,302,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +175,220,rower_favoritecharts_view,See favorite charts,TRUE,302,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 176,222,workout_workflow_config2_view,configure workflow,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,FALSE, -177,224,workflow_default_view,resets workflow to default,TRUE,302,basic,302,302,FALSE,403,403,FALSE,302,403,FALSE,FALSE,FALSE,FALSE,TRUE, -178,225,sendmail,feedback form,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -180,232,laboratory_view,lab,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +177,224,workflow_default_view,resets workflow to default,TRUE,302,basic,302,302,FALSE,403,403,FALSE,302,403,FALSE,FALSE,FALSE,TRUE,TRUE, +178,225,sendmail,feedback form,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +180,232,laboratory_view,lab,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 181,233,errormessage_view,not used,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 182,237,payment_confirm_view,confirm payment,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 183,238,upgrade_confirm_view,confirm upgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, @@ -187,21 +187,21 @@ 187,243,downgrade_view,confirm downgrade,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 188,244,payment_completed_view,payment completed,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 189,245,downgrade_completed_view,downgrade completed,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -190,246,paidplans_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +190,246,paidplans_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, 191,247,plan_stop_view,stop plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 192,248,plan_tobasic_view,reset to basic,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 193,249,checkouts_view,checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 194,250,upgrade_checkouts_view,upgrade checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 195,251,downgrade_checkouts_view,downgrade checkout,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -196,252,planrequired_view,a plan is required,TRUE,302,basic,302,302,basic,302,302,coach,302,302,FALSE,FALSE,FALSE,FALSE,TRUE, +196,252,planrequired_view,a plan is required,TRUE,302,basic,302,302,basic,302,302,coach,302,302,FALSE,FALSE,FALSE,TRUE,TRUE, 197,253,start_trial_view,payments,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 198,254,start_plantrial_view,paid plans,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -199,256,rower_register_view,register rower,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -200,257,freecoach_register_view,register coach,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, -201,259,workout_workflow_view,workout workflow vieq,TRUE,302,basic,200,200,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,FALSE,TRUE, -202,260,workout_flexchart3_view,flex chart,TRUE,302,basic,200,403,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,FALSE,TRUE, +199,256,rower_register_view,register rower,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +200,257,freecoach_register_view,register coach,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +201,259,workout_workflow_view,workout workflow vieq,TRUE,302,basic,200,200,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,TRUE,TRUE, +202,260,workout_flexchart3_view,flex chart,TRUE,302,basic,200,403,basic,200,200,coach,200,200,FALSE,FALSE,TRUE,TRUE,TRUE, 203,264,rower_process_testcallback,test callback,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -204,265,rower_create_trainingplan,create training plan,TRUE,302,plan,200,302,FALSE,403,403,FALSE,200,403,FALSE,FALSE,FALSE,FALSE,TRUE, +204,265,rower_create_trainingplan,create training plan,TRUE,302,plan,200,302,FALSE,403,403,FALSE,200,403,FALSE,FALSE,FALSE,TRUE,TRUE, 205,267,TrainingPlanDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 206,268,MicroCycleDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 207,269,MesoCycleDelete,delete training plan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, @@ -217,11 +217,11 @@ 217,289,TrainingPlanUpdate,update trainingplan,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 218,290,TrainingTargetUpdate,update training target,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 219,291,strokedataform,test strokedata,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -220,292,plannedsession_teamcreate_view,create planned session for team,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,FALSE,TRUE, +220,292,plannedsession_teamcreate_view,create planned session for team,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, 221,296,plannedsession_teamedit_view,edit planned sesssion,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -222,298,plannedsession_create_view,create planned session ,TRUE,302,plan,200,302,FALSE,200,302,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, -223,300,plannedsession_multiclone_view,clone multiple planned sessions,TRUE,302,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, -224,302,plannedsession_multicreate_view,create multiple planned sessions,TRUE,302,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,TRUE, +222,298,plannedsession_create_view,create planned session ,TRUE,302,plan,200,302,FALSE,200,302,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +223,300,plannedsession_multiclone_view,clone multiple planned sessions,TRUE,302,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +224,302,plannedsession_multicreate_view,create multiple planned sessions,TRUE,302,plan,200,302,plan,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 225,305,plannedsession_edit_view,edit planned sesssion,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 226,306,plannedsession_totemplate_view,planned session to template,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 227,307,plannedsession_compare_view,compare workouts from planned session,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, @@ -234,77 +234,77 @@ 234,323,plannedsessions_coach_view,team view,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 235,325,plannedsessions_coach_icsemail_view,team view,TRUE,302,coach,302,403,coach,302,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE, 236,326,plannedsessions_print_view,print view,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, -237,327,plannedsession_comment_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -238,330,plannedsessions_icsemail_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -239,332,plannedsessions_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, -240,334,course_edit_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -241,335,course_delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -242,336,course_kmldownload_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -243,337,course_replace_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -244,338,course_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -245,339,course_map_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -246,341,c2listdebug_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -247,343,rower_process_callback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -248,344,rower_process_stravacallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -249,345,rower_process_sporttrackscallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -250,346,rower_process_underarmourcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -251,347,rower_process_polarcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -252,348,rower_process_runkeepercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -253,349,rower_process_tpcallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -254,350,rower_process_twittercallback,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -255,351,set_language,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -256,352,SetOffsetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -257,353,stats,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -258,354,stats_json,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -259,355,jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -260,356,workers,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -261,357,worker_details,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -262,358,finished_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -263,359,started_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -264,360,deferred_jobs,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -265,361,clear_queue,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -266,362,requeue_all,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -267,363,job_detail,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -268,364,delete_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -269,365,actions,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -270,366,requeue_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -271,367,enqueue_job,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -272,368,JavaScriptCatalog,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -273,369,serve,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -274,371,LoginView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -275,372,LogoutView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -276,373,PasswordChangeView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -277,374,PasswordChangeDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -278,375,PasswordResetView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -279,376,PasswordResetDoneView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -280,377,PasswordResetConfirmView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -281,378,PasswordResetCompleteView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -282,386,index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -283,387,login,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -284,388,logout,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -285,389,password_change,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -286,390,password_change_done,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -287,391,i18n_javascript,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -288,392,shortcut,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -289,393,changelist_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -290,394,add_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -291,395,autocomplete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -292,396,history_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -293,397,delete_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -294,398,change_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -295,399,RedirectView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -296,568,app_index,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -297,571,landingview,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -298,573,get_blog_posts,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -299,576,AuthorizationView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -300,577,TokenView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -301,578,WorkoutViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -302,582,RowerViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -303,586,FavoriteChartViewSet,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -304,590,APIRootView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -305,592,SwaggerSchemaView,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -306,595,strokedatajson,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, -307,596,error500_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -308,603,failed_queue_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -309,604,failed_queue_empty,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, -310,605,failed_job_view,,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +237,327,plannedsession_comment_view,comment on planned session,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +238,330,plannedsessions_icsemail_view,send ICS email,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,FALSE,FALSE, +239,332,plannedsessions_view,View planned sessions,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +240,334,course_edit_view,Edit course,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +241,335,course_delete_view,Delete course,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +242,336,course_kmldownload_view,Kmldownload course,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +243,337,course_replace_view,Replace course,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +244,338,course_view, view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +245,339,course_map_view,Map course,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +246,341,c2listdebug_view,Debug view,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +247,343,rower_process_callback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +248,344,rower_process_stravacallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +249,345,rower_process_sporttrackscallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +250,346,rower_process_underarmourcallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +251,347,rower_process_polarcallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +252,348,rower_process_runkeepercallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +253,349,rower_process_tpcallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +254,350,rower_process_twittercallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +255,351,set_language,Set language,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +256,352,SetOffsetView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +257,353,stats,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +258,354,stats_json,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +259,355,jobs,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +260,356,workers,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +261,357,worker_details,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +262,358,finished_jobs,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +263,359,started_jobs,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +264,360,deferred_jobs,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +265,361,clear_queue,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +266,362,requeue_all,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +267,363,job_detail,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +268,364,delete_job,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +269,365,actions,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +270,366,requeue_job_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +271,367,enqueue_job,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +272,368,JavaScriptCatalog,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +273,369,serve,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +274,371,LoginView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +275,372,LogoutView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +276,373,PasswordChangeView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +277,374,PasswordChangeDoneView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +278,375,PasswordResetView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +279,376,PasswordResetDoneView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +280,377,PasswordResetConfirmView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +281,378,PasswordResetCompleteView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +282,386,index,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +283,387,login,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +284,388,logout,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +285,389,password_change,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +286,390,password_change_done,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +287,391,i18n_javascript,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +288,392,shortcut,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +289,393,changelist_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +290,394,add_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +291,395,autocomplete_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +292,396,history_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +293,397,delete_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +294,398,change_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +295,399,RedirectView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +296,568,app_index,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +297,571,landingview,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +298,573,get_blog_posts,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +299,576,AuthorizationView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +300,577,TokenView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +301,578,WorkoutViewSet,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +302,582,RowerViewSet,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +303,586,FavoriteChartViewSet,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +304,590,APIRootView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +305,592,SwaggerSchemaView,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +306,595,strokedatajson,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, +307,596,error500_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +308,603,failed_queue_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +309,604,failed_queue_empty,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, +310,605,failed_job_view,Other Apps views,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE, From 6bdf9216f230b29f4344c4501b268fefe965e643 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 8 Feb 2020 09:03:34 +0100 Subject: [PATCH 79/80] mocking request to weather API --- 008fc633-d9d4-4abc-9812-e22014e19498.tcx | 2523 ---------------------- 03f8abfb-ccec-45bf-ac29-b193477d2045.tcx | 2523 ---------------------- 23099b58-7632-4d34-97e2-2f4890cf5773.tcx | 2523 ---------------------- b90680ee-35d5-4633-9974-fdc4bba4c68f.tcx | 2523 ---------------------- c6c032ca-da24-42cf-b61f-02816b7e2ccd.tcx | 2523 ---------------------- requirements.txt | 305 ++- rowers/tests/mocks.py | 1 + rowers/tests/test_aworkouts.py | 4 +- rowers/tests/test_permissions.py | 4 +- rowers/views/workoutviews.py | 17 +- rowers/weather.py | 35 +- 11 files changed, 238 insertions(+), 12743 deletions(-) delete mode 100644 008fc633-d9d4-4abc-9812-e22014e19498.tcx delete mode 100644 03f8abfb-ccec-45bf-ac29-b193477d2045.tcx delete mode 100644 23099b58-7632-4d34-97e2-2f4890cf5773.tcx delete mode 100644 b90680ee-35d5-4633-9974-fdc4bba4c68f.tcx delete mode 100644 c6c032ca-da24-42cf-b61f-02816b7e2ccd.tcx diff --git a/008fc633-d9d4-4abc-9812-e22014e19498.tcx b/008fc633-d9d4-4abc-9812-e22014e19498.tcx deleted file mode 100644 index 31d671d7..00000000 --- a/008fc633-d9d4-4abc-9812-e22014e19498.tcx +++ /dev/null @@ -1,2523 +0,0 @@ - - - - - 2016-05-20T13:41:26.962390+00:00 - - 537 - 2000 - 1 - - 148 - - - 156 - - Active - 21 - Manual - - - - 5.4 - - 127 - - 0 - - - 19 - - - - - - 13.1 - - 127 - - 19 - - - 26 - - - - - - 21.0 - - 128 - - 20 - - - 45 - - - - - - 30.3 - - 129 - - 20 - - - 64 - - - - - - 39.0 - - 130 - - 20 - - - 74 - - - - - - 48.2 - - 131 - - 21 - - - 80 - - - - - - 57.6 - - 131 - - 20 - - - 83 - - - - - - 66.4 - - 132 - - 20 - - - 87 - - - - - - 75.5 - - 132 - - 21 - - - 86 - - - - - - 85.1 - - 132 - - 20 - - - 88 - - - - - - 95.0 - - 132 - - 21 - - - 100 - - - - - - 105.0 - - 133 - - 22 - - - 127 - - - - - - 115.3 - - 134 - - 21 - - - 135 - - - - - - 125.8 - - 135 - - 21 - - - 139 - - - - - - 136.6 - - 136 - - 21 - - - 146 - - - - - - 147.2 - - 137 - - 22 - - - 150 - - - - - - 157.6 - - 139 - - 22 - - - 152 - - - - - - 167.8 - - 140 - - 21 - - - 146 - - - - - - 178.5 - - 140 - - 22 - - - 150 - - - - - - 188.5 - - 141 - - 21 - - - 155 - - - - - - 199.3 - - 141 - - 21 - - - 148 - - - - - - 209.4 - - 142 - - 22 - - - 151 - - - - - - 219.4 - - 142 - - 22 - - - 151 - - - - - - 230.2 - - 143 - - 22 - - - 148 - - - - - - 240.2 - - 144 - - 22 - - - 147 - - - - - - 250.1 - - 145 - - 23 - - - 150 - - - - - - 259.6 - - 145 - - 23 - - - 152 - - - - - - 270.3 - - 145 - - 23 - - - 152 - - - - - - 280.6 - - 145 - - 22 - - - 149 - - - - - - 290.7 - - 144 - - 22 - - - 150 - - - - - - 300.8 - - 145 - - 23 - - - 149 - - - - - - 311.1 - - 145 - - 22 - - - 152 - - - - - - 321.2 - - 145 - - 22 - - - 157 - - - - - - 331.9 - - 145 - - 21 - - - 150 - - - - - - 342.0 - - 146 - - 22 - - - 151 - - - - - - 352.4 - - 146 - - 22 - - - 151 - - - - - - 363.0 - - 146 - - 22 - - - 153 - - - - - - 373.4 - - 147 - - 22 - - - 152 - - - - - - 383.9 - - 147 - - 22 - - - 153 - - - - - - 394.6 - - 147 - - 22 - - - 152 - - - - - - 405.0 - - 147 - - 21 - - - 149 - - - - - - 415.3 - - 148 - - 22 - - - 152 - - - - - - 426.0 - - 148 - - 22 - - - 151 - - - - - - 436.5 - - 148 - - 21 - - - 149 - - - - - - 446.9 - - 148 - - 22 - - - 149 - - - - - - 456.9 - - 149 - - 22 - - - 156 - - - - - - 467.6 - - 149 - - 22 - - - 155 - - - - - - 478.5 - - 150 - - 22 - - - 156 - - - - - - 489.0 - - 150 - - 21 - - - 154 - - - - - - 499.1 - - 150 - - 21 - - - 148 - - - - - - 510.0 - - 150 - - 22 - - - 151 - - - - - - 519.9 - - 149 - - 22 - - - 153 - - - - - - 530.6 - - 149 - - 22 - - - 151 - - - - - - 540.8 - - 149 - - 22 - - - 148 - - - - - - 550.8 - - 148 - - 22 - - - 149 - - - - - - 560.8 - - 148 - - 22 - - - 144 - - - - - - 571.0 - - 147 - - 22 - - - 149 - - - - - - 580.7 - - 147 - - 22 - - - 150 - - - - - - 591.2 - - 147 - - 22 - - - 151 - - - - - - 601.4 - - 147 - - 22 - - - 150 - - - - - - 611.4 - - 147 - - 23 - - - 153 - - - - - - 621.8 - - 147 - - 23 - - - 151 - - - - - - 632.1 - - 147 - - 22 - - - 155 - - - - - - 642.3 - - 147 - - 22 - - - 154 - - - - - - 652.0 - - 148 - - 23 - - - 157 - - - - - - 662.7 - - 148 - - 23 - - - 162 - - - - - - 673.1 - - 148 - - 23 - - - 163 - - - - - - 683.6 - - 149 - - 22 - - - 163 - - - - - - 693.8 - - 149 - - 22 - - - 162 - - - - - - 703.8 - - 149 - - 22 - - - 164 - - - - - - 714.7 - - 150 - - 23 - - - 162 - - - - - - 724.9 - - 150 - - 22 - - - 162 - - - - - - 735.2 - - 151 - - 23 - - - 159 - - - - - - 745.4 - - 151 - - 22 - - - 158 - - - - - - 756.0 - - 151 - - 23 - - - 164 - - - - - - 766.3 - - 150 - - 22 - - - 163 - - - - - - 776.5 - - 150 - - 22 - - - 161 - - - - - - 786.9 - - 150 - - 23 - - - 163 - - - - - - 797.2 - - 150 - - 22 - - - 165 - - - - - - 807.8 - - 150 - - 23 - - - 166 - - - - - - 818.2 - - 150 - - 23 - - - 166 - - - - - - 828.4 - - 150 - - 22 - - - 168 - - - - - - 839.2 - - 150 - - 23 - - - 169 - - - - - - 849.6 - - 151 - - 23 - - - 166 - - - - - - 860.1 - - 151 - - 22 - - - 172 - - - - - - 870.3 - - 152 - - 22 - - - 172 - - - - - - 881.1 - - 152 - - 22 - - - 169 - - - - - - 891.7 - - 152 - - 23 - - - 167 - - - - - - 902.1 - - 152 - - 22 - - - 164 - - - - - - 913.1 - - 152 - - 22 - - - 161 - - - - - - 923.9 - - 153 - - 22 - - - 158 - - - - - - 934.6 - - 154 - - 21 - - - 158 - - - - - - 945.4 - - 154 - - 21 - - - 154 - - - - - - 956.0 - - 155 - - 21 - - - 155 - - - - - - 966.7 - - 155 - - 21 - - - 152 - - - - - - 977.4 - - 156 - - 21 - - - 150 - - - - - - 988.1 - - 156 - - 21 - - - 157 - - - - - - 998.8 - - 156 - - 21 - - - 155 - - - - - - 1009.6 - - 156 - - 21 - - - 151 - - - - - - 1020.6 - - 156 - - 21 - - - 147 - - - - - - 1031.5 - - 156 - - 20 - - - 145 - - - - - - 1042.5 - - 156 - - 21 - - - 144 - - - - - - 1053.3 - - 155 - - 20 - - - 145 - - - - - - 1064.1 - - 155 - - 21 - - - 147 - - - - - - 1075.3 - - 155 - - 20 - - - 142 - - - - - - 1086.1 - - 155 - - 20 - - - 136 - - - - - - 1097.5 - - 155 - - 21 - - - 141 - - - - - - 1108.5 - - 155 - - 20 - - - 146 - - - - - - 1119.2 - - 155 - - 20 - - - 143 - - - - - - 1130.6 - - 155 - - 20 - - - 143 - - - - - - 1141.3 - - 155 - - 20 - - - 143 - - - - - - 1152.4 - - 155 - - 21 - - - 142 - - - - - - 1163.3 - - 155 - - 20 - - - 138 - - - - - - 1173.8 - - 154 - - 20 - - - 141 - - - - - - 1184.8 - - 154 - - 21 - - - 146 - - - - - - 1195.8 - - 153 - - 21 - - - 146 - - - - - - 1206.6 - - 152 - - 21 - - - 141 - - - - - - 1217.3 - - 153 - - 21 - - - 141 - - - - - - 1227.8 - - 152 - - 21 - - - 140 - - - - - - 1238.7 - - 152 - - 21 - - - 143 - - - - - - 1249.5 - - 151 - - 21 - - - 149 - - - - - - 1260.1 - - 151 - - 20 - - - 141 - - - - - - 1270.9 - - 151 - - 21 - - - 141 - - - - - - 1281.8 - - 150 - - 21 - - - 145 - - - - - - 1292.7 - - 151 - - 20 - - - 142 - - - - - - 1303.4 - - 151 - - 20 - - - 141 - - - - - - 1314.3 - - 151 - - 21 - - - 141 - - - - - - 1325.2 - - 151 - - 21 - - - 146 - - - - - - 1336.1 - - 152 - - 20 - - - 143 - - - - - - 1346.9 - - 152 - - 21 - - - 144 - - - - - - 1357.4 - - 152 - - 20 - - - 141 - - - - - - 1368.1 - - 152 - - 21 - - - 138 - - - - - - 1379.0 - - 152 - - 20 - - - 142 - - - - - - 1389.5 - - 153 - - 21 - - - 145 - - - - - - 1399.9 - - 152 - - 21 - - - 138 - - - - - - 1410.7 - - 152 - - 20 - - - 139 - - - - - - 1422.0 - - 152 - - 20 - - - 139 - - - - - - 1432.8 - - 151 - - 20 - - - 141 - - - - - - 1443.6 - - 151 - - 21 - - - 146 - - - - - - 1454.4 - - 152 - - 20 - - - 143 - - - - - - 1465.1 - - 151 - - 21 - - - 143 - - - - - - 1475.9 - - 152 - - 21 - - - 145 - - - - - - 1486.6 - - 152 - - 21 - - - 148 - - - - - - 1497.4 - - 153 - - 21 - - - 143 - - - - - - 1508.2 - - 153 - - 20 - - - 140 - - - - - - 1519.2 - - 154 - - 20 - - - 144 - - - - - - 1530.0 - - 154 - - 21 - - - 143 - - - - - - 1540.9 - - 153 - - 20 - - - 141 - - - - - - 1551.3 - - 153 - - 21 - - - 143 - - - - - - 1562.6 - - 153 - - 21 - - - 146 - - - - - - 1573.3 - - 153 - - 20 - - - 141 - - - - - - 1584.2 - - 152 - - 20 - - - 139 - - - - - - 1594.6 - - 152 - - 21 - - - 145 - - - - - - 1606.0 - - 152 - - 21 - - - 143 - - - - - - 1616.2 - - 152 - - 20 - - - 138 - - - - - - 1627.4 - - 152 - - 21 - - - 140 - - - - - - 1638.0 - - 152 - - 21 - - - 144 - - - - - - 1649.2 - - 151 - - 20 - - - 143 - - - - - - 1660.2 - - 152 - - 20 - - - 143 - - - - - - 1670.8 - - 151 - - 20 - - - 142 - - - - - - 1681.4 - - 151 - - 21 - - - 140 - - - - - - 1692.1 - - 151 - - 21 - - - 140 - - - - - - 1702.5 - - 150 - - 21 - - - 141 - - - - - - 1713.7 - - 150 - - 21 - - - 144 - - - - - - 1724.4 - - 150 - - 21 - - - 146 - - - - - - 1735.1 - - 150 - - 20 - - - 141 - - - - - - 1745.6 - - 150 - - 21 - - - 140 - - - - - - 1756.3 - - 150 - - 21 - - - 141 - - - - - - 1766.2 - - 151 - - 20 - - - 142 - - - - - - 1777.1 - - 150 - - 22 - - - 138 - - - - - - 1787.5 - - 150 - - 21 - - - 138 - - - - - - 1797.7 - - 150 - - 22 - - - 140 - - - - - - 1808.4 - - 150 - - 21 - - - 140 - - - - - - 1818.4 - - 149 - - 21 - - - 138 - - - - - - 1828.9 - - 149 - - 22 - - - 146 - - - - - - 1839.9 - - 149 - - 21 - - - 142 - - - - - - 1850.5 - - 148 - - 21 - - - 142 - - - - - - 1861.2 - - 148 - - 21 - - - 145 - - - - - - 1871.9 - - 147 - - 21 - - - 143 - - - - - - 1882.6 - - 147 - - 20 - - - 139 - - - - - - 1893.3 - - 148 - - 20 - - - 140 - - - - - - 1904.3 - - 149 - - 21 - - - 144 - - - - - - 1915.4 - - 149 - - 20 - - - 148 - - - - - - 1926.2 - - 150 - - 20 - - - 139 - - - - - - 1937.3 - - 151 - - 20 - - - 140 - - - - - - 1947.8 - - 152 - - 20 - - - 144 - - - - - - 1959.1 - - 152 - - 20 - - - 142 - - - - - - 1969.8 - - 153 - - 20 - - - 140 - - - - - - 1980.6 - - 153 - - 21 - - - 143 - - - - - - 1991.4 - - 153 - - 21 - - - 143 - - - - - - 2000.0 - - 154 - - 21 - - - 147 - - - - - - <Element 'Notes' at 0x7f5eb3c3ef98> - - - - rowsandall.com/rowingdata - - - rowingdata - - - 0 - 75 - - Release - - EN - 000-00000-00 - - diff --git a/03f8abfb-ccec-45bf-ac29-b193477d2045.tcx b/03f8abfb-ccec-45bf-ac29-b193477d2045.tcx deleted file mode 100644 index eb0a1c3e..00000000 --- a/03f8abfb-ccec-45bf-ac29-b193477d2045.tcx +++ /dev/null @@ -1,2523 +0,0 @@ - - - - - 2016-05-20T13:41:26.962390+00:00 - - 537 - 2000 - 1 - - 148 - - - 156 - - Active - 21 - Manual - - - - 5.4 - - 127 - - 0 - - - 19 - - - - - - 13.1 - - 127 - - 19 - - - 26 - - - - - - 21.0 - - 128 - - 20 - - - 45 - - - - - - 30.3 - - 129 - - 20 - - - 64 - - - - - - 39.0 - - 130 - - 20 - - - 74 - - - - - - 48.2 - - 131 - - 21 - - - 80 - - - - - - 57.6 - - 131 - - 20 - - - 83 - - - - - - 66.4 - - 132 - - 20 - - - 87 - - - - - - 75.5 - - 132 - - 21 - - - 86 - - - - - - 85.1 - - 132 - - 20 - - - 88 - - - - - - 95.0 - - 132 - - 21 - - - 100 - - - - - - 105.0 - - 133 - - 22 - - - 127 - - - - - - 115.3 - - 134 - - 21 - - - 135 - - - - - - 125.8 - - 135 - - 21 - - - 139 - - - - - - 136.6 - - 136 - - 21 - - - 146 - - - - - - 147.2 - - 137 - - 22 - - - 150 - - - - - - 157.6 - - 139 - - 22 - - - 152 - - - - - - 167.8 - - 140 - - 21 - - - 146 - - - - - - 178.5 - - 140 - - 22 - - - 150 - - - - - - 188.5 - - 141 - - 21 - - - 155 - - - - - - 199.3 - - 141 - - 21 - - - 148 - - - - - - 209.4 - - 142 - - 22 - - - 151 - - - - - - 219.4 - - 142 - - 22 - - - 151 - - - - - - 230.2 - - 143 - - 22 - - - 148 - - - - - - 240.2 - - 144 - - 22 - - - 147 - - - - - - 250.1 - - 145 - - 23 - - - 150 - - - - - - 259.6 - - 145 - - 23 - - - 152 - - - - - - 270.3 - - 145 - - 23 - - - 152 - - - - - - 280.6 - - 145 - - 22 - - - 149 - - - - - - 290.7 - - 144 - - 22 - - - 150 - - - - - - 300.8 - - 145 - - 23 - - - 149 - - - - - - 311.1 - - 145 - - 22 - - - 152 - - - - - - 321.2 - - 145 - - 22 - - - 157 - - - - - - 331.9 - - 145 - - 21 - - - 150 - - - - - - 342.0 - - 146 - - 22 - - - 151 - - - - - - 352.4 - - 146 - - 22 - - - 151 - - - - - - 363.0 - - 146 - - 22 - - - 153 - - - - - - 373.4 - - 147 - - 22 - - - 152 - - - - - - 383.9 - - 147 - - 22 - - - 153 - - - - - - 394.6 - - 147 - - 22 - - - 152 - - - - - - 405.0 - - 147 - - 21 - - - 149 - - - - - - 415.3 - - 148 - - 22 - - - 152 - - - - - - 426.0 - - 148 - - 22 - - - 151 - - - - - - 436.5 - - 148 - - 21 - - - 149 - - - - - - 446.9 - - 148 - - 22 - - - 149 - - - - - - 456.9 - - 149 - - 22 - - - 156 - - - - - - 467.6 - - 149 - - 22 - - - 155 - - - - - - 478.5 - - 150 - - 22 - - - 156 - - - - - - 489.0 - - 150 - - 21 - - - 154 - - - - - - 499.1 - - 150 - - 21 - - - 148 - - - - - - 510.0 - - 150 - - 22 - - - 151 - - - - - - 519.9 - - 149 - - 22 - - - 153 - - - - - - 530.6 - - 149 - - 22 - - - 151 - - - - - - 540.8 - - 149 - - 22 - - - 148 - - - - - - 550.8 - - 148 - - 22 - - - 149 - - - - - - 560.8 - - 148 - - 22 - - - 144 - - - - - - 571.0 - - 147 - - 22 - - - 149 - - - - - - 580.7 - - 147 - - 22 - - - 150 - - - - - - 591.2 - - 147 - - 22 - - - 151 - - - - - - 601.4 - - 147 - - 22 - - - 150 - - - - - - 611.4 - - 147 - - 23 - - - 153 - - - - - - 621.8 - - 147 - - 23 - - - 151 - - - - - - 632.1 - - 147 - - 22 - - - 155 - - - - - - 642.3 - - 147 - - 22 - - - 154 - - - - - - 652.0 - - 148 - - 23 - - - 157 - - - - - - 662.7 - - 148 - - 23 - - - 162 - - - - - - 673.1 - - 148 - - 23 - - - 163 - - - - - - 683.6 - - 149 - - 22 - - - 163 - - - - - - 693.8 - - 149 - - 22 - - - 162 - - - - - - 703.8 - - 149 - - 22 - - - 164 - - - - - - 714.7 - - 150 - - 23 - - - 162 - - - - - - 724.9 - - 150 - - 22 - - - 162 - - - - - - 735.2 - - 151 - - 23 - - - 159 - - - - - - 745.4 - - 151 - - 22 - - - 158 - - - - - - 756.0 - - 151 - - 23 - - - 164 - - - - - - 766.3 - - 150 - - 22 - - - 163 - - - - - - 776.5 - - 150 - - 22 - - - 161 - - - - - - 786.9 - - 150 - - 23 - - - 163 - - - - - - 797.2 - - 150 - - 22 - - - 165 - - - - - - 807.8 - - 150 - - 23 - - - 166 - - - - - - 818.2 - - 150 - - 23 - - - 166 - - - - - - 828.4 - - 150 - - 22 - - - 168 - - - - - - 839.2 - - 150 - - 23 - - - 169 - - - - - - 849.6 - - 151 - - 23 - - - 166 - - - - - - 860.1 - - 151 - - 22 - - - 172 - - - - - - 870.3 - - 152 - - 22 - - - 172 - - - - - - 881.1 - - 152 - - 22 - - - 169 - - - - - - 891.7 - - 152 - - 23 - - - 167 - - - - - - 902.1 - - 152 - - 22 - - - 164 - - - - - - 913.1 - - 152 - - 22 - - - 161 - - - - - - 923.9 - - 153 - - 22 - - - 158 - - - - - - 934.6 - - 154 - - 21 - - - 158 - - - - - - 945.4 - - 154 - - 21 - - - 154 - - - - - - 956.0 - - 155 - - 21 - - - 155 - - - - - - 966.7 - - 155 - - 21 - - - 152 - - - - - - 977.4 - - 156 - - 21 - - - 150 - - - - - - 988.1 - - 156 - - 21 - - - 157 - - - - - - 998.8 - - 156 - - 21 - - - 155 - - - - - - 1009.6 - - 156 - - 21 - - - 151 - - - - - - 1020.6 - - 156 - - 21 - - - 147 - - - - - - 1031.5 - - 156 - - 20 - - - 145 - - - - - - 1042.5 - - 156 - - 21 - - - 144 - - - - - - 1053.3 - - 155 - - 20 - - - 145 - - - - - - 1064.1 - - 155 - - 21 - - - 147 - - - - - - 1075.3 - - 155 - - 20 - - - 142 - - - - - - 1086.1 - - 155 - - 20 - - - 136 - - - - - - 1097.5 - - 155 - - 21 - - - 141 - - - - - - 1108.5 - - 155 - - 20 - - - 146 - - - - - - 1119.2 - - 155 - - 20 - - - 143 - - - - - - 1130.6 - - 155 - - 20 - - - 143 - - - - - - 1141.3 - - 155 - - 20 - - - 143 - - - - - - 1152.4 - - 155 - - 21 - - - 142 - - - - - - 1163.3 - - 155 - - 20 - - - 138 - - - - - - 1173.8 - - 154 - - 20 - - - 141 - - - - - - 1184.8 - - 154 - - 21 - - - 146 - - - - - - 1195.8 - - 153 - - 21 - - - 146 - - - - - - 1206.6 - - 152 - - 21 - - - 141 - - - - - - 1217.3 - - 153 - - 21 - - - 141 - - - - - - 1227.8 - - 152 - - 21 - - - 140 - - - - - - 1238.7 - - 152 - - 21 - - - 143 - - - - - - 1249.5 - - 151 - - 21 - - - 149 - - - - - - 1260.1 - - 151 - - 20 - - - 141 - - - - - - 1270.9 - - 151 - - 21 - - - 141 - - - - - - 1281.8 - - 150 - - 21 - - - 145 - - - - - - 1292.7 - - 151 - - 20 - - - 142 - - - - - - 1303.4 - - 151 - - 20 - - - 141 - - - - - - 1314.3 - - 151 - - 21 - - - 141 - - - - - - 1325.2 - - 151 - - 21 - - - 146 - - - - - - 1336.1 - - 152 - - 20 - - - 143 - - - - - - 1346.9 - - 152 - - 21 - - - 144 - - - - - - 1357.4 - - 152 - - 20 - - - 141 - - - - - - 1368.1 - - 152 - - 21 - - - 138 - - - - - - 1379.0 - - 152 - - 20 - - - 142 - - - - - - 1389.5 - - 153 - - 21 - - - 145 - - - - - - 1399.9 - - 152 - - 21 - - - 138 - - - - - - 1410.7 - - 152 - - 20 - - - 139 - - - - - - 1422.0 - - 152 - - 20 - - - 139 - - - - - - 1432.8 - - 151 - - 20 - - - 141 - - - - - - 1443.6 - - 151 - - 21 - - - 146 - - - - - - 1454.4 - - 152 - - 20 - - - 143 - - - - - - 1465.1 - - 151 - - 21 - - - 143 - - - - - - 1475.9 - - 152 - - 21 - - - 145 - - - - - - 1486.6 - - 152 - - 21 - - - 148 - - - - - - 1497.4 - - 153 - - 21 - - - 143 - - - - - - 1508.2 - - 153 - - 20 - - - 140 - - - - - - 1519.2 - - 154 - - 20 - - - 144 - - - - - - 1530.0 - - 154 - - 21 - - - 143 - - - - - - 1540.9 - - 153 - - 20 - - - 141 - - - - - - 1551.3 - - 153 - - 21 - - - 143 - - - - - - 1562.6 - - 153 - - 21 - - - 146 - - - - - - 1573.3 - - 153 - - 20 - - - 141 - - - - - - 1584.2 - - 152 - - 20 - - - 139 - - - - - - 1594.6 - - 152 - - 21 - - - 145 - - - - - - 1606.0 - - 152 - - 21 - - - 143 - - - - - - 1616.2 - - 152 - - 20 - - - 138 - - - - - - 1627.4 - - 152 - - 21 - - - 140 - - - - - - 1638.0 - - 152 - - 21 - - - 144 - - - - - - 1649.2 - - 151 - - 20 - - - 143 - - - - - - 1660.2 - - 152 - - 20 - - - 143 - - - - - - 1670.8 - - 151 - - 20 - - - 142 - - - - - - 1681.4 - - 151 - - 21 - - - 140 - - - - - - 1692.1 - - 151 - - 21 - - - 140 - - - - - - 1702.5 - - 150 - - 21 - - - 141 - - - - - - 1713.7 - - 150 - - 21 - - - 144 - - - - - - 1724.4 - - 150 - - 21 - - - 146 - - - - - - 1735.1 - - 150 - - 20 - - - 141 - - - - - - 1745.6 - - 150 - - 21 - - - 140 - - - - - - 1756.3 - - 150 - - 21 - - - 141 - - - - - - 1766.2 - - 151 - - 20 - - - 142 - - - - - - 1777.1 - - 150 - - 22 - - - 138 - - - - - - 1787.5 - - 150 - - 21 - - - 138 - - - - - - 1797.7 - - 150 - - 22 - - - 140 - - - - - - 1808.4 - - 150 - - 21 - - - 140 - - - - - - 1818.4 - - 149 - - 21 - - - 138 - - - - - - 1828.9 - - 149 - - 22 - - - 146 - - - - - - 1839.9 - - 149 - - 21 - - - 142 - - - - - - 1850.5 - - 148 - - 21 - - - 142 - - - - - - 1861.2 - - 148 - - 21 - - - 145 - - - - - - 1871.9 - - 147 - - 21 - - - 143 - - - - - - 1882.6 - - 147 - - 20 - - - 139 - - - - - - 1893.3 - - 148 - - 20 - - - 140 - - - - - - 1904.3 - - 149 - - 21 - - - 144 - - - - - - 1915.4 - - 149 - - 20 - - - 148 - - - - - - 1926.2 - - 150 - - 20 - - - 139 - - - - - - 1937.3 - - 151 - - 20 - - - 140 - - - - - - 1947.8 - - 152 - - 20 - - - 144 - - - - - - 1959.1 - - 152 - - 20 - - - 142 - - - - - - 1969.8 - - 153 - - 20 - - - 140 - - - - - - 1980.6 - - 153 - - 21 - - - 143 - - - - - - 1991.4 - - 153 - - 21 - - - 143 - - - - - - 2000.0 - - 154 - - 21 - - - 147 - - - - - - <Element 'Notes' at 0x13b50a20> - - - - rowsandall.com/rowingdata - - - rowingdata - - - 0 - 75 - - Release - - EN - 000-00000-00 - - diff --git a/23099b58-7632-4d34-97e2-2f4890cf5773.tcx b/23099b58-7632-4d34-97e2-2f4890cf5773.tcx deleted file mode 100644 index e7df305a..00000000 --- a/23099b58-7632-4d34-97e2-2f4890cf5773.tcx +++ /dev/null @@ -1,2523 +0,0 @@ - - - - - 2016-05-20T13:41:26.962390+00:00 - - 537 - 2000 - 118 - - 148 - - - 156 - - Active - 21 - Manual - - - - 5.4 - - 127 - - 0 - - - 19 - - - - - - 13.1 - - 127 - - 19 - - - 26 - - - - - - 21.0 - - 128 - - 20 - - - 45 - - - - - - 30.3 - - 129 - - 20 - - - 64 - - - - - - 39.0 - - 130 - - 20 - - - 74 - - - - - - 48.2 - - 131 - - 21 - - - 80 - - - - - - 57.6 - - 131 - - 20 - - - 83 - - - - - - 66.4 - - 132 - - 20 - - - 87 - - - - - - 75.5 - - 132 - - 21 - - - 86 - - - - - - 85.1 - - 132 - - 20 - - - 88 - - - - - - 95.0 - - 132 - - 21 - - - 100 - - - - - - 105.0 - - 133 - - 22 - - - 127 - - - - - - 115.3 - - 134 - - 21 - - - 135 - - - - - - 125.8 - - 135 - - 21 - - - 139 - - - - - - 136.6 - - 136 - - 21 - - - 146 - - - - - - 147.2 - - 137 - - 22 - - - 150 - - - - - - 157.6 - - 139 - - 22 - - - 152 - - - - - - 167.8 - - 140 - - 21 - - - 146 - - - - - - 178.5 - - 140 - - 22 - - - 150 - - - - - - 188.5 - - 141 - - 21 - - - 155 - - - - - - 199.3 - - 141 - - 21 - - - 148 - - - - - - 209.4 - - 142 - - 22 - - - 151 - - - - - - 219.4 - - 142 - - 22 - - - 151 - - - - - - 230.2 - - 143 - - 22 - - - 148 - - - - - - 240.2 - - 144 - - 22 - - - 147 - - - - - - 250.1 - - 145 - - 23 - - - 150 - - - - - - 259.6 - - 145 - - 23 - - - 152 - - - - - - 270.3 - - 145 - - 23 - - - 152 - - - - - - 280.6 - - 145 - - 22 - - - 149 - - - - - - 290.7 - - 144 - - 22 - - - 150 - - - - - - 300.8 - - 145 - - 23 - - - 149 - - - - - - 311.1 - - 145 - - 22 - - - 152 - - - - - - 321.2 - - 145 - - 22 - - - 157 - - - - - - 331.9 - - 145 - - 21 - - - 150 - - - - - - 342.0 - - 146 - - 22 - - - 151 - - - - - - 352.4 - - 146 - - 22 - - - 151 - - - - - - 363.0 - - 146 - - 22 - - - 153 - - - - - - 373.4 - - 147 - - 22 - - - 152 - - - - - - 383.9 - - 147 - - 22 - - - 153 - - - - - - 394.6 - - 147 - - 22 - - - 152 - - - - - - 405.0 - - 147 - - 21 - - - 149 - - - - - - 415.3 - - 148 - - 22 - - - 152 - - - - - - 426.0 - - 148 - - 22 - - - 151 - - - - - - 436.5 - - 148 - - 21 - - - 149 - - - - - - 446.9 - - 148 - - 22 - - - 149 - - - - - - 456.9 - - 149 - - 22 - - - 156 - - - - - - 467.6 - - 149 - - 22 - - - 155 - - - - - - 478.5 - - 150 - - 22 - - - 156 - - - - - - 489.0 - - 150 - - 21 - - - 154 - - - - - - 499.1 - - 150 - - 21 - - - 148 - - - - - - 510.0 - - 150 - - 22 - - - 151 - - - - - - 519.9 - - 149 - - 22 - - - 153 - - - - - - 530.6 - - 149 - - 22 - - - 151 - - - - - - 540.8 - - 149 - - 22 - - - 148 - - - - - - 550.8 - - 148 - - 22 - - - 149 - - - - - - 560.8 - - 148 - - 22 - - - 144 - - - - - - 571.0 - - 147 - - 22 - - - 149 - - - - - - 580.7 - - 147 - - 22 - - - 150 - - - - - - 591.2 - - 147 - - 22 - - - 151 - - - - - - 601.4 - - 147 - - 22 - - - 150 - - - - - - 611.4 - - 147 - - 23 - - - 153 - - - - - - 621.8 - - 147 - - 23 - - - 151 - - - - - - 632.1 - - 147 - - 22 - - - 155 - - - - - - 642.3 - - 147 - - 22 - - - 154 - - - - - - 652.0 - - 148 - - 23 - - - 157 - - - - - - 662.7 - - 148 - - 23 - - - 162 - - - - - - 673.1 - - 148 - - 23 - - - 163 - - - - - - 683.6 - - 149 - - 22 - - - 163 - - - - - - 693.8 - - 149 - - 22 - - - 162 - - - - - - 703.8 - - 149 - - 22 - - - 164 - - - - - - 714.7 - - 150 - - 23 - - - 162 - - - - - - 724.9 - - 150 - - 22 - - - 162 - - - - - - 735.2 - - 151 - - 23 - - - 159 - - - - - - 745.4 - - 151 - - 22 - - - 158 - - - - - - 756.0 - - 151 - - 23 - - - 164 - - - - - - 766.3 - - 150 - - 22 - - - 163 - - - - - - 776.5 - - 150 - - 22 - - - 161 - - - - - - 786.9 - - 150 - - 23 - - - 163 - - - - - - 797.2 - - 150 - - 22 - - - 165 - - - - - - 807.8 - - 150 - - 23 - - - 166 - - - - - - 818.2 - - 150 - - 23 - - - 166 - - - - - - 828.4 - - 150 - - 22 - - - 168 - - - - - - 839.2 - - 150 - - 23 - - - 169 - - - - - - 849.6 - - 151 - - 23 - - - 166 - - - - - - 860.1 - - 151 - - 22 - - - 172 - - - - - - 870.3 - - 152 - - 22 - - - 172 - - - - - - 881.1 - - 152 - - 22 - - - 169 - - - - - - 891.7 - - 152 - - 23 - - - 167 - - - - - - 902.1 - - 152 - - 22 - - - 164 - - - - - - 913.1 - - 152 - - 22 - - - 161 - - - - - - 923.9 - - 153 - - 22 - - - 158 - - - - - - 934.6 - - 154 - - 21 - - - 158 - - - - - - 945.4 - - 154 - - 21 - - - 154 - - - - - - 956.0 - - 155 - - 21 - - - 155 - - - - - - 966.7 - - 155 - - 21 - - - 152 - - - - - - 977.4 - - 156 - - 21 - - - 150 - - - - - - 988.1 - - 156 - - 21 - - - 157 - - - - - - 998.8 - - 156 - - 21 - - - 155 - - - - - - 1009.6 - - 156 - - 21 - - - 151 - - - - - - 1020.6 - - 156 - - 21 - - - 147 - - - - - - 1031.5 - - 156 - - 20 - - - 145 - - - - - - 1042.5 - - 156 - - 21 - - - 144 - - - - - - 1053.3 - - 155 - - 20 - - - 145 - - - - - - 1064.1 - - 155 - - 21 - - - 147 - - - - - - 1075.3 - - 155 - - 20 - - - 142 - - - - - - 1086.1 - - 155 - - 20 - - - 136 - - - - - - 1097.5 - - 155 - - 21 - - - 141 - - - - - - 1108.5 - - 155 - - 20 - - - 146 - - - - - - 1119.2 - - 155 - - 20 - - - 143 - - - - - - 1130.6 - - 155 - - 20 - - - 143 - - - - - - 1141.3 - - 155 - - 20 - - - 143 - - - - - - 1152.4 - - 155 - - 21 - - - 142 - - - - - - 1163.3 - - 155 - - 20 - - - 138 - - - - - - 1173.8 - - 154 - - 20 - - - 141 - - - - - - 1184.8 - - 154 - - 21 - - - 146 - - - - - - 1195.8 - - 153 - - 21 - - - 146 - - - - - - 1206.6 - - 152 - - 21 - - - 141 - - - - - - 1217.3 - - 153 - - 21 - - - 141 - - - - - - 1227.8 - - 152 - - 21 - - - 140 - - - - - - 1238.7 - - 152 - - 21 - - - 143 - - - - - - 1249.5 - - 151 - - 21 - - - 149 - - - - - - 1260.1 - - 151 - - 20 - - - 141 - - - - - - 1270.9 - - 151 - - 21 - - - 141 - - - - - - 1281.8 - - 150 - - 21 - - - 145 - - - - - - 1292.7 - - 151 - - 20 - - - 142 - - - - - - 1303.4 - - 151 - - 20 - - - 141 - - - - - - 1314.3 - - 151 - - 21 - - - 141 - - - - - - 1325.2 - - 151 - - 21 - - - 146 - - - - - - 1336.1 - - 152 - - 20 - - - 143 - - - - - - 1346.9 - - 152 - - 21 - - - 144 - - - - - - 1357.4 - - 152 - - 20 - - - 141 - - - - - - 1368.1 - - 152 - - 21 - - - 138 - - - - - - 1379.0 - - 152 - - 20 - - - 142 - - - - - - 1389.5 - - 153 - - 21 - - - 145 - - - - - - 1399.9 - - 152 - - 21 - - - 138 - - - - - - 1410.7 - - 152 - - 20 - - - 139 - - - - - - 1422.0 - - 152 - - 20 - - - 139 - - - - - - 1432.8 - - 151 - - 20 - - - 141 - - - - - - 1443.6 - - 151 - - 21 - - - 146 - - - - - - 1454.4 - - 152 - - 20 - - - 143 - - - - - - 1465.1 - - 151 - - 21 - - - 143 - - - - - - 1475.9 - - 152 - - 21 - - - 145 - - - - - - 1486.6 - - 152 - - 21 - - - 148 - - - - - - 1497.4 - - 153 - - 21 - - - 143 - - - - - - 1508.2 - - 153 - - 20 - - - 140 - - - - - - 1519.2 - - 154 - - 20 - - - 144 - - - - - - 1530.0 - - 154 - - 21 - - - 143 - - - - - - 1540.9 - - 153 - - 20 - - - 141 - - - - - - 1551.3 - - 153 - - 21 - - - 143 - - - - - - 1562.6 - - 153 - - 21 - - - 146 - - - - - - 1573.3 - - 153 - - 20 - - - 141 - - - - - - 1584.2 - - 152 - - 20 - - - 139 - - - - - - 1594.6 - - 152 - - 21 - - - 145 - - - - - - 1606.0 - - 152 - - 21 - - - 143 - - - - - - 1616.2 - - 152 - - 20 - - - 138 - - - - - - 1627.4 - - 152 - - 21 - - - 140 - - - - - - 1638.0 - - 152 - - 21 - - - 144 - - - - - - 1649.2 - - 151 - - 20 - - - 143 - - - - - - 1660.2 - - 152 - - 20 - - - 143 - - - - - - 1670.8 - - 151 - - 20 - - - 142 - - - - - - 1681.4 - - 151 - - 21 - - - 140 - - - - - - 1692.1 - - 151 - - 21 - - - 140 - - - - - - 1702.5 - - 150 - - 21 - - - 141 - - - - - - 1713.7 - - 150 - - 21 - - - 144 - - - - - - 1724.4 - - 150 - - 21 - - - 146 - - - - - - 1735.1 - - 150 - - 20 - - - 141 - - - - - - 1745.6 - - 150 - - 21 - - - 140 - - - - - - 1756.3 - - 150 - - 21 - - - 141 - - - - - - 1766.2 - - 151 - - 20 - - - 142 - - - - - - 1777.1 - - 150 - - 22 - - - 138 - - - - - - 1787.5 - - 150 - - 21 - - - 138 - - - - - - 1797.7 - - 150 - - 22 - - - 140 - - - - - - 1808.4 - - 150 - - 21 - - - 140 - - - - - - 1818.4 - - 149 - - 21 - - - 138 - - - - - - 1828.9 - - 149 - - 22 - - - 146 - - - - - - 1839.9 - - 149 - - 21 - - - 142 - - - - - - 1850.5 - - 148 - - 21 - - - 142 - - - - - - 1861.2 - - 148 - - 21 - - - 145 - - - - - - 1871.9 - - 147 - - 21 - - - 143 - - - - - - 1882.6 - - 147 - - 20 - - - 139 - - - - - - 1893.3 - - 148 - - 20 - - - 140 - - - - - - 1904.3 - - 149 - - 21 - - - 144 - - - - - - 1915.4 - - 149 - - 20 - - - 148 - - - - - - 1926.2 - - 150 - - 20 - - - 139 - - - - - - 1937.3 - - 151 - - 20 - - - 140 - - - - - - 1947.8 - - 152 - - 20 - - - 144 - - - - - - 1959.1 - - 152 - - 20 - - - 142 - - - - - - 1969.8 - - 153 - - 20 - - - 140 - - - - - - 1980.6 - - 153 - - 21 - - - 143 - - - - - - 1991.4 - - 153 - - 21 - - - 143 - - - - - - 2000.0 - - 154 - - 21 - - - 147 - - - - - - <Element 'Notes' at 0x7f433c451b38> - - - - rowsandall.com/rowingdata - - - rowingdata - - - 0 - 75 - - Release - - EN - 000-00000-00 - - diff --git a/b90680ee-35d5-4633-9974-fdc4bba4c68f.tcx b/b90680ee-35d5-4633-9974-fdc4bba4c68f.tcx deleted file mode 100644 index 97ac8d4f..00000000 --- a/b90680ee-35d5-4633-9974-fdc4bba4c68f.tcx +++ /dev/null @@ -1,2523 +0,0 @@ - - - - - 2016-05-20T13:41:26.962390+00:00 - - 537 - 2000 - 1 - - 148 - - - 156 - - Active - 21 - Manual - - - - 5.4 - - 127 - - 0 - - - 19 - - - - - - 13.1 - - 127 - - 19 - - - 26 - - - - - - 21.0 - - 128 - - 20 - - - 45 - - - - - - 30.3 - - 129 - - 20 - - - 64 - - - - - - 39.0 - - 130 - - 20 - - - 74 - - - - - - 48.2 - - 131 - - 21 - - - 80 - - - - - - 57.6 - - 131 - - 20 - - - 83 - - - - - - 66.4 - - 132 - - 20 - - - 87 - - - - - - 75.5 - - 132 - - 21 - - - 86 - - - - - - 85.1 - - 132 - - 20 - - - 88 - - - - - - 95.0 - - 132 - - 21 - - - 100 - - - - - - 105.0 - - 133 - - 22 - - - 127 - - - - - - 115.3 - - 134 - - 21 - - - 135 - - - - - - 125.8 - - 135 - - 21 - - - 139 - - - - - - 136.6 - - 136 - - 21 - - - 146 - - - - - - 147.2 - - 137 - - 22 - - - 150 - - - - - - 157.6 - - 139 - - 22 - - - 152 - - - - - - 167.8 - - 140 - - 21 - - - 146 - - - - - - 178.5 - - 140 - - 22 - - - 150 - - - - - - 188.5 - - 141 - - 21 - - - 155 - - - - - - 199.3 - - 141 - - 21 - - - 148 - - - - - - 209.4 - - 142 - - 22 - - - 151 - - - - - - 219.4 - - 142 - - 22 - - - 151 - - - - - - 230.2 - - 143 - - 22 - - - 148 - - - - - - 240.2 - - 144 - - 22 - - - 147 - - - - - - 250.1 - - 145 - - 23 - - - 150 - - - - - - 259.6 - - 145 - - 23 - - - 152 - - - - - - 270.3 - - 145 - - 23 - - - 152 - - - - - - 280.6 - - 145 - - 22 - - - 149 - - - - - - 290.7 - - 144 - - 22 - - - 150 - - - - - - 300.8 - - 145 - - 23 - - - 149 - - - - - - 311.1 - - 145 - - 22 - - - 152 - - - - - - 321.2 - - 145 - - 22 - - - 157 - - - - - - 331.9 - - 145 - - 21 - - - 150 - - - - - - 342.0 - - 146 - - 22 - - - 151 - - - - - - 352.4 - - 146 - - 22 - - - 151 - - - - - - 363.0 - - 146 - - 22 - - - 153 - - - - - - 373.4 - - 147 - - 22 - - - 152 - - - - - - 383.9 - - 147 - - 22 - - - 153 - - - - - - 394.6 - - 147 - - 22 - - - 152 - - - - - - 405.0 - - 147 - - 21 - - - 149 - - - - - - 415.3 - - 148 - - 22 - - - 152 - - - - - - 426.0 - - 148 - - 22 - - - 151 - - - - - - 436.5 - - 148 - - 21 - - - 149 - - - - - - 446.9 - - 148 - - 22 - - - 149 - - - - - - 456.9 - - 149 - - 22 - - - 156 - - - - - - 467.6 - - 149 - - 22 - - - 155 - - - - - - 478.5 - - 150 - - 22 - - - 156 - - - - - - 489.0 - - 150 - - 21 - - - 154 - - - - - - 499.1 - - 150 - - 21 - - - 148 - - - - - - 510.0 - - 150 - - 22 - - - 151 - - - - - - 519.9 - - 149 - - 22 - - - 153 - - - - - - 530.6 - - 149 - - 22 - - - 151 - - - - - - 540.8 - - 149 - - 22 - - - 148 - - - - - - 550.8 - - 148 - - 22 - - - 149 - - - - - - 560.8 - - 148 - - 22 - - - 144 - - - - - - 571.0 - - 147 - - 22 - - - 149 - - - - - - 580.7 - - 147 - - 22 - - - 150 - - - - - - 591.2 - - 147 - - 22 - - - 151 - - - - - - 601.4 - - 147 - - 22 - - - 150 - - - - - - 611.4 - - 147 - - 23 - - - 153 - - - - - - 621.8 - - 147 - - 23 - - - 151 - - - - - - 632.1 - - 147 - - 22 - - - 155 - - - - - - 642.3 - - 147 - - 22 - - - 154 - - - - - - 652.0 - - 148 - - 23 - - - 157 - - - - - - 662.7 - - 148 - - 23 - - - 162 - - - - - - 673.1 - - 148 - - 23 - - - 163 - - - - - - 683.6 - - 149 - - 22 - - - 163 - - - - - - 693.8 - - 149 - - 22 - - - 162 - - - - - - 703.8 - - 149 - - 22 - - - 164 - - - - - - 714.7 - - 150 - - 23 - - - 162 - - - - - - 724.9 - - 150 - - 22 - - - 162 - - - - - - 735.2 - - 151 - - 23 - - - 159 - - - - - - 745.4 - - 151 - - 22 - - - 158 - - - - - - 756.0 - - 151 - - 23 - - - 164 - - - - - - 766.3 - - 150 - - 22 - - - 163 - - - - - - 776.5 - - 150 - - 22 - - - 161 - - - - - - 786.9 - - 150 - - 23 - - - 163 - - - - - - 797.2 - - 150 - - 22 - - - 165 - - - - - - 807.8 - - 150 - - 23 - - - 166 - - - - - - 818.2 - - 150 - - 23 - - - 166 - - - - - - 828.4 - - 150 - - 22 - - - 168 - - - - - - 839.2 - - 150 - - 23 - - - 169 - - - - - - 849.6 - - 151 - - 23 - - - 166 - - - - - - 860.1 - - 151 - - 22 - - - 172 - - - - - - 870.3 - - 152 - - 22 - - - 172 - - - - - - 881.1 - - 152 - - 22 - - - 169 - - - - - - 891.7 - - 152 - - 23 - - - 167 - - - - - - 902.1 - - 152 - - 22 - - - 164 - - - - - - 913.1 - - 152 - - 22 - - - 161 - - - - - - 923.9 - - 153 - - 22 - - - 158 - - - - - - 934.6 - - 154 - - 21 - - - 158 - - - - - - 945.4 - - 154 - - 21 - - - 154 - - - - - - 956.0 - - 155 - - 21 - - - 155 - - - - - - 966.7 - - 155 - - 21 - - - 152 - - - - - - 977.4 - - 156 - - 21 - - - 150 - - - - - - 988.1 - - 156 - - 21 - - - 157 - - - - - - 998.8 - - 156 - - 21 - - - 155 - - - - - - 1009.6 - - 156 - - 21 - - - 151 - - - - - - 1020.6 - - 156 - - 21 - - - 147 - - - - - - 1031.5 - - 156 - - 20 - - - 145 - - - - - - 1042.5 - - 156 - - 21 - - - 144 - - - - - - 1053.3 - - 155 - - 20 - - - 145 - - - - - - 1064.1 - - 155 - - 21 - - - 147 - - - - - - 1075.3 - - 155 - - 20 - - - 142 - - - - - - 1086.1 - - 155 - - 20 - - - 136 - - - - - - 1097.5 - - 155 - - 21 - - - 141 - - - - - - 1108.5 - - 155 - - 20 - - - 146 - - - - - - 1119.2 - - 155 - - 20 - - - 143 - - - - - - 1130.6 - - 155 - - 20 - - - 143 - - - - - - 1141.3 - - 155 - - 20 - - - 143 - - - - - - 1152.4 - - 155 - - 21 - - - 142 - - - - - - 1163.3 - - 155 - - 20 - - - 138 - - - - - - 1173.8 - - 154 - - 20 - - - 141 - - - - - - 1184.8 - - 154 - - 21 - - - 146 - - - - - - 1195.8 - - 153 - - 21 - - - 146 - - - - - - 1206.6 - - 152 - - 21 - - - 141 - - - - - - 1217.3 - - 153 - - 21 - - - 141 - - - - - - 1227.8 - - 152 - - 21 - - - 140 - - - - - - 1238.7 - - 152 - - 21 - - - 143 - - - - - - 1249.5 - - 151 - - 21 - - - 149 - - - - - - 1260.1 - - 151 - - 20 - - - 141 - - - - - - 1270.9 - - 151 - - 21 - - - 141 - - - - - - 1281.8 - - 150 - - 21 - - - 145 - - - - - - 1292.7 - - 151 - - 20 - - - 142 - - - - - - 1303.4 - - 151 - - 20 - - - 141 - - - - - - 1314.3 - - 151 - - 21 - - - 141 - - - - - - 1325.2 - - 151 - - 21 - - - 146 - - - - - - 1336.1 - - 152 - - 20 - - - 143 - - - - - - 1346.9 - - 152 - - 21 - - - 144 - - - - - - 1357.4 - - 152 - - 20 - - - 141 - - - - - - 1368.1 - - 152 - - 21 - - - 138 - - - - - - 1379.0 - - 152 - - 20 - - - 142 - - - - - - 1389.5 - - 153 - - 21 - - - 145 - - - - - - 1399.9 - - 152 - - 21 - - - 138 - - - - - - 1410.7 - - 152 - - 20 - - - 139 - - - - - - 1422.0 - - 152 - - 20 - - - 139 - - - - - - 1432.8 - - 151 - - 20 - - - 141 - - - - - - 1443.6 - - 151 - - 21 - - - 146 - - - - - - 1454.4 - - 152 - - 20 - - - 143 - - - - - - 1465.1 - - 151 - - 21 - - - 143 - - - - - - 1475.9 - - 152 - - 21 - - - 145 - - - - - - 1486.6 - - 152 - - 21 - - - 148 - - - - - - 1497.4 - - 153 - - 21 - - - 143 - - - - - - 1508.2 - - 153 - - 20 - - - 140 - - - - - - 1519.2 - - 154 - - 20 - - - 144 - - - - - - 1530.0 - - 154 - - 21 - - - 143 - - - - - - 1540.9 - - 153 - - 20 - - - 141 - - - - - - 1551.3 - - 153 - - 21 - - - 143 - - - - - - 1562.6 - - 153 - - 21 - - - 146 - - - - - - 1573.3 - - 153 - - 20 - - - 141 - - - - - - 1584.2 - - 152 - - 20 - - - 139 - - - - - - 1594.6 - - 152 - - 21 - - - 145 - - - - - - 1606.0 - - 152 - - 21 - - - 143 - - - - - - 1616.2 - - 152 - - 20 - - - 138 - - - - - - 1627.4 - - 152 - - 21 - - - 140 - - - - - - 1638.0 - - 152 - - 21 - - - 144 - - - - - - 1649.2 - - 151 - - 20 - - - 143 - - - - - - 1660.2 - - 152 - - 20 - - - 143 - - - - - - 1670.8 - - 151 - - 20 - - - 142 - - - - - - 1681.4 - - 151 - - 21 - - - 140 - - - - - - 1692.1 - - 151 - - 21 - - - 140 - - - - - - 1702.5 - - 150 - - 21 - - - 141 - - - - - - 1713.7 - - 150 - - 21 - - - 144 - - - - - - 1724.4 - - 150 - - 21 - - - 146 - - - - - - 1735.1 - - 150 - - 20 - - - 141 - - - - - - 1745.6 - - 150 - - 21 - - - 140 - - - - - - 1756.3 - - 150 - - 21 - - - 141 - - - - - - 1766.2 - - 151 - - 20 - - - 142 - - - - - - 1777.1 - - 150 - - 22 - - - 138 - - - - - - 1787.5 - - 150 - - 21 - - - 138 - - - - - - 1797.7 - - 150 - - 22 - - - 140 - - - - - - 1808.4 - - 150 - - 21 - - - 140 - - - - - - 1818.4 - - 149 - - 21 - - - 138 - - - - - - 1828.9 - - 149 - - 22 - - - 146 - - - - - - 1839.9 - - 149 - - 21 - - - 142 - - - - - - 1850.5 - - 148 - - 21 - - - 142 - - - - - - 1861.2 - - 148 - - 21 - - - 145 - - - - - - 1871.9 - - 147 - - 21 - - - 143 - - - - - - 1882.6 - - 147 - - 20 - - - 139 - - - - - - 1893.3 - - 148 - - 20 - - - 140 - - - - - - 1904.3 - - 149 - - 21 - - - 144 - - - - - - 1915.4 - - 149 - - 20 - - - 148 - - - - - - 1926.2 - - 150 - - 20 - - - 139 - - - - - - 1937.3 - - 151 - - 20 - - - 140 - - - - - - 1947.8 - - 152 - - 20 - - - 144 - - - - - - 1959.1 - - 152 - - 20 - - - 142 - - - - - - 1969.8 - - 153 - - 20 - - - 140 - - - - - - 1980.6 - - 153 - - 21 - - - 143 - - - - - - 1991.4 - - 153 - - 21 - - - 143 - - - - - - 2000.0 - - 154 - - 21 - - - 147 - - - - - - <Element 'Notes' at 0x000002C71C48FE58> - - - - rowsandall.com/rowingdata - - - rowingdata - - - 0 - 75 - - Release - - EN - 000-00000-00 - - diff --git a/c6c032ca-da24-42cf-b61f-02816b7e2ccd.tcx b/c6c032ca-da24-42cf-b61f-02816b7e2ccd.tcx deleted file mode 100644 index 98408a5a..00000000 --- a/c6c032ca-da24-42cf-b61f-02816b7e2ccd.tcx +++ /dev/null @@ -1,2523 +0,0 @@ - - - - - 2016-05-20T13:41:26.962390+00:00 - - 537 - 2000 - 1 - - 148 - - - 156 - - Active - 21 - Manual - - - - 5.4 - - 127 - - 0 - - - 19 - - - - - - 13.1 - - 127 - - 19 - - - 26 - - - - - - 21.0 - - 128 - - 20 - - - 45 - - - - - - 30.3 - - 129 - - 20 - - - 64 - - - - - - 39.0 - - 130 - - 20 - - - 74 - - - - - - 48.2 - - 131 - - 21 - - - 80 - - - - - - 57.6 - - 131 - - 20 - - - 83 - - - - - - 66.4 - - 132 - - 20 - - - 87 - - - - - - 75.5 - - 132 - - 21 - - - 86 - - - - - - 85.1 - - 132 - - 20 - - - 88 - - - - - - 95.0 - - 132 - - 21 - - - 100 - - - - - - 105.0 - - 133 - - 22 - - - 127 - - - - - - 115.3 - - 134 - - 21 - - - 135 - - - - - - 125.8 - - 135 - - 21 - - - 139 - - - - - - 136.6 - - 136 - - 21 - - - 146 - - - - - - 147.2 - - 137 - - 22 - - - 150 - - - - - - 157.6 - - 139 - - 22 - - - 152 - - - - - - 167.8 - - 140 - - 21 - - - 146 - - - - - - 178.5 - - 140 - - 22 - - - 150 - - - - - - 188.5 - - 141 - - 21 - - - 155 - - - - - - 199.3 - - 141 - - 21 - - - 148 - - - - - - 209.4 - - 142 - - 22 - - - 151 - - - - - - 219.4 - - 142 - - 22 - - - 151 - - - - - - 230.2 - - 143 - - 22 - - - 148 - - - - - - 240.2 - - 144 - - 22 - - - 147 - - - - - - 250.1 - - 145 - - 23 - - - 150 - - - - - - 259.6 - - 145 - - 23 - - - 152 - - - - - - 270.3 - - 145 - - 23 - - - 152 - - - - - - 280.6 - - 145 - - 22 - - - 149 - - - - - - 290.7 - - 144 - - 22 - - - 150 - - - - - - 300.8 - - 145 - - 23 - - - 149 - - - - - - 311.1 - - 145 - - 22 - - - 152 - - - - - - 321.2 - - 145 - - 22 - - - 157 - - - - - - 331.9 - - 145 - - 21 - - - 150 - - - - - - 342.0 - - 146 - - 22 - - - 151 - - - - - - 352.4 - - 146 - - 22 - - - 151 - - - - - - 363.0 - - 146 - - 22 - - - 153 - - - - - - 373.4 - - 147 - - 22 - - - 152 - - - - - - 383.9 - - 147 - - 22 - - - 153 - - - - - - 394.6 - - 147 - - 22 - - - 152 - - - - - - 405.0 - - 147 - - 21 - - - 149 - - - - - - 415.3 - - 148 - - 22 - - - 152 - - - - - - 426.0 - - 148 - - 22 - - - 151 - - - - - - 436.5 - - 148 - - 21 - - - 149 - - - - - - 446.9 - - 148 - - 22 - - - 149 - - - - - - 456.9 - - 149 - - 22 - - - 156 - - - - - - 467.6 - - 149 - - 22 - - - 155 - - - - - - 478.5 - - 150 - - 22 - - - 156 - - - - - - 489.0 - - 150 - - 21 - - - 154 - - - - - - 499.1 - - 150 - - 21 - - - 148 - - - - - - 510.0 - - 150 - - 22 - - - 151 - - - - - - 519.9 - - 149 - - 22 - - - 153 - - - - - - 530.6 - - 149 - - 22 - - - 151 - - - - - - 540.8 - - 149 - - 22 - - - 148 - - - - - - 550.8 - - 148 - - 22 - - - 149 - - - - - - 560.8 - - 148 - - 22 - - - 144 - - - - - - 571.0 - - 147 - - 22 - - - 149 - - - - - - 580.7 - - 147 - - 22 - - - 150 - - - - - - 591.2 - - 147 - - 22 - - - 151 - - - - - - 601.4 - - 147 - - 22 - - - 150 - - - - - - 611.4 - - 147 - - 23 - - - 153 - - - - - - 621.8 - - 147 - - 23 - - - 151 - - - - - - 632.1 - - 147 - - 22 - - - 155 - - - - - - 642.3 - - 147 - - 22 - - - 154 - - - - - - 652.0 - - 148 - - 23 - - - 157 - - - - - - 662.7 - - 148 - - 23 - - - 162 - - - - - - 673.1 - - 148 - - 23 - - - 163 - - - - - - 683.6 - - 149 - - 22 - - - 163 - - - - - - 693.8 - - 149 - - 22 - - - 162 - - - - - - 703.8 - - 149 - - 22 - - - 164 - - - - - - 714.7 - - 150 - - 23 - - - 162 - - - - - - 724.9 - - 150 - - 22 - - - 162 - - - - - - 735.2 - - 151 - - 23 - - - 159 - - - - - - 745.4 - - 151 - - 22 - - - 158 - - - - - - 756.0 - - 151 - - 23 - - - 164 - - - - - - 766.3 - - 150 - - 22 - - - 163 - - - - - - 776.5 - - 150 - - 22 - - - 161 - - - - - - 786.9 - - 150 - - 23 - - - 163 - - - - - - 797.2 - - 150 - - 22 - - - 165 - - - - - - 807.8 - - 150 - - 23 - - - 166 - - - - - - 818.2 - - 150 - - 23 - - - 166 - - - - - - 828.4 - - 150 - - 22 - - - 168 - - - - - - 839.2 - - 150 - - 23 - - - 169 - - - - - - 849.6 - - 151 - - 23 - - - 166 - - - - - - 860.1 - - 151 - - 22 - - - 172 - - - - - - 870.3 - - 152 - - 22 - - - 172 - - - - - - 881.1 - - 152 - - 22 - - - 169 - - - - - - 891.7 - - 152 - - 23 - - - 167 - - - - - - 902.1 - - 152 - - 22 - - - 164 - - - - - - 913.1 - - 152 - - 22 - - - 161 - - - - - - 923.9 - - 153 - - 22 - - - 158 - - - - - - 934.6 - - 154 - - 21 - - - 158 - - - - - - 945.4 - - 154 - - 21 - - - 154 - - - - - - 956.0 - - 155 - - 21 - - - 155 - - - - - - 966.7 - - 155 - - 21 - - - 152 - - - - - - 977.4 - - 156 - - 21 - - - 150 - - - - - - 988.1 - - 156 - - 21 - - - 157 - - - - - - 998.8 - - 156 - - 21 - - - 155 - - - - - - 1009.6 - - 156 - - 21 - - - 151 - - - - - - 1020.6 - - 156 - - 21 - - - 147 - - - - - - 1031.5 - - 156 - - 20 - - - 145 - - - - - - 1042.5 - - 156 - - 21 - - - 144 - - - - - - 1053.3 - - 155 - - 20 - - - 145 - - - - - - 1064.1 - - 155 - - 21 - - - 147 - - - - - - 1075.3 - - 155 - - 20 - - - 142 - - - - - - 1086.1 - - 155 - - 20 - - - 136 - - - - - - 1097.5 - - 155 - - 21 - - - 141 - - - - - - 1108.5 - - 155 - - 20 - - - 146 - - - - - - 1119.2 - - 155 - - 20 - - - 143 - - - - - - 1130.6 - - 155 - - 20 - - - 143 - - - - - - 1141.3 - - 155 - - 20 - - - 143 - - - - - - 1152.4 - - 155 - - 21 - - - 142 - - - - - - 1163.3 - - 155 - - 20 - - - 138 - - - - - - 1173.8 - - 154 - - 20 - - - 141 - - - - - - 1184.8 - - 154 - - 21 - - - 146 - - - - - - 1195.8 - - 153 - - 21 - - - 146 - - - - - - 1206.6 - - 152 - - 21 - - - 141 - - - - - - 1217.3 - - 153 - - 21 - - - 141 - - - - - - 1227.8 - - 152 - - 21 - - - 140 - - - - - - 1238.7 - - 152 - - 21 - - - 143 - - - - - - 1249.5 - - 151 - - 21 - - - 149 - - - - - - 1260.1 - - 151 - - 20 - - - 141 - - - - - - 1270.9 - - 151 - - 21 - - - 141 - - - - - - 1281.8 - - 150 - - 21 - - - 145 - - - - - - 1292.7 - - 151 - - 20 - - - 142 - - - - - - 1303.4 - - 151 - - 20 - - - 141 - - - - - - 1314.3 - - 151 - - 21 - - - 141 - - - - - - 1325.2 - - 151 - - 21 - - - 146 - - - - - - 1336.1 - - 152 - - 20 - - - 143 - - - - - - 1346.9 - - 152 - - 21 - - - 144 - - - - - - 1357.4 - - 152 - - 20 - - - 141 - - - - - - 1368.1 - - 152 - - 21 - - - 138 - - - - - - 1379.0 - - 152 - - 20 - - - 142 - - - - - - 1389.5 - - 153 - - 21 - - - 145 - - - - - - 1399.9 - - 152 - - 21 - - - 138 - - - - - - 1410.7 - - 152 - - 20 - - - 139 - - - - - - 1422.0 - - 152 - - 20 - - - 139 - - - - - - 1432.8 - - 151 - - 20 - - - 141 - - - - - - 1443.6 - - 151 - - 21 - - - 146 - - - - - - 1454.4 - - 152 - - 20 - - - 143 - - - - - - 1465.1 - - 151 - - 21 - - - 143 - - - - - - 1475.9 - - 152 - - 21 - - - 145 - - - - - - 1486.6 - - 152 - - 21 - - - 148 - - - - - - 1497.4 - - 153 - - 21 - - - 143 - - - - - - 1508.2 - - 153 - - 20 - - - 140 - - - - - - 1519.2 - - 154 - - 20 - - - 144 - - - - - - 1530.0 - - 154 - - 21 - - - 143 - - - - - - 1540.9 - - 153 - - 20 - - - 141 - - - - - - 1551.3 - - 153 - - 21 - - - 143 - - - - - - 1562.6 - - 153 - - 21 - - - 146 - - - - - - 1573.3 - - 153 - - 20 - - - 141 - - - - - - 1584.2 - - 152 - - 20 - - - 139 - - - - - - 1594.6 - - 152 - - 21 - - - 145 - - - - - - 1606.0 - - 152 - - 21 - - - 143 - - - - - - 1616.2 - - 152 - - 20 - - - 138 - - - - - - 1627.4 - - 152 - - 21 - - - 140 - - - - - - 1638.0 - - 152 - - 21 - - - 144 - - - - - - 1649.2 - - 151 - - 20 - - - 143 - - - - - - 1660.2 - - 152 - - 20 - - - 143 - - - - - - 1670.8 - - 151 - - 20 - - - 142 - - - - - - 1681.4 - - 151 - - 21 - - - 140 - - - - - - 1692.1 - - 151 - - 21 - - - 140 - - - - - - 1702.5 - - 150 - - 21 - - - 141 - - - - - - 1713.7 - - 150 - - 21 - - - 144 - - - - - - 1724.4 - - 150 - - 21 - - - 146 - - - - - - 1735.1 - - 150 - - 20 - - - 141 - - - - - - 1745.6 - - 150 - - 21 - - - 140 - - - - - - 1756.3 - - 150 - - 21 - - - 141 - - - - - - 1766.2 - - 151 - - 20 - - - 142 - - - - - - 1777.1 - - 150 - - 22 - - - 138 - - - - - - 1787.5 - - 150 - - 21 - - - 138 - - - - - - 1797.7 - - 150 - - 22 - - - 140 - - - - - - 1808.4 - - 150 - - 21 - - - 140 - - - - - - 1818.4 - - 149 - - 21 - - - 138 - - - - - - 1828.9 - - 149 - - 22 - - - 146 - - - - - - 1839.9 - - 149 - - 21 - - - 142 - - - - - - 1850.5 - - 148 - - 21 - - - 142 - - - - - - 1861.2 - - 148 - - 21 - - - 145 - - - - - - 1871.9 - - 147 - - 21 - - - 143 - - - - - - 1882.6 - - 147 - - 20 - - - 139 - - - - - - 1893.3 - - 148 - - 20 - - - 140 - - - - - - 1904.3 - - 149 - - 21 - - - 144 - - - - - - 1915.4 - - 149 - - 20 - - - 148 - - - - - - 1926.2 - - 150 - - 20 - - - 139 - - - - - - 1937.3 - - 151 - - 20 - - - 140 - - - - - - 1947.8 - - 152 - - 20 - - - 144 - - - - - - 1959.1 - - 152 - - 20 - - - 142 - - - - - - 1969.8 - - 153 - - 20 - - - 140 - - - - - - 1980.6 - - 153 - - 21 - - - 143 - - - - - - 1991.4 - - 153 - - 21 - - - 143 - - - - - - 2000.0 - - 154 - - 21 - - - 147 - - - - - - <Element 'Notes' at 0x15038e80> - - - - rowsandall.com/rowingdata - - - rowingdata - - - 0 - 75 - - Release - - EN - 000-00000-00 - - diff --git a/requirements.txt b/requirements.txt index 00ed095f..8ebc3c9b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,138 +1,217 @@ -amqp==2.5.2 -anytree==2.8.0 -apparmor==2.13.3 +amqp==2.4.2 +apipkg==1.5 appdirs==1.4.3 -asn1crypto==1.3.0 -attrs==19.3.0 +arcgis==1.6.0 +arrow==0.13.1 +asn1crypto==0.24.0 +atomicwrites==1.3.0 +attrs==19.1.0 backcall==0.1.0 -beautifulsoup4==4.8.2 -billiard==3.6.1.0 +beautifulsoup4==4.7.1 +billiard==3.6.0.0 bleach==3.1.0 +bokeh==1.0.4 boto==2.49.0 -btrfsutil==1.1.1 -CacheControl==0.12.6 -case==1.5.3 -catfish==1.4.13 +braintree==3.55.0 +cairocffi==1.0.2 celery==4.3.0 -ceph-volume==1.0.0 -cephfs==2.0.0 -cephfs-shell==0.0.1 -certifi==2019.11.28 -cffi==1.13.2 +certifi==2019.3.9 +cffi==1.12.2 chardet==3.0.4 -colorama==0.4.3 -contextlib2==0.6.0 -cryptography==2.8 +Click==7.0 +cloudpickle==1.2.2 +colorama==0.4.1 +colorclass==2.2.0 +cookies==2.2.1 +coreapi==2.3.3 +coreschema==0.0.4 +coverage==4.5.3 +cryptography==2.6.1 cycler==0.10.0 -decorator==4.4.1 -defusedxml==0.6.0 -distlib==0.3.0 -distro==1.4.0 +dask==2.6.0 +decorator==4.4.0 +defusedxml==0.5.0 +Django==2.1.7 +django-analytical==2.5.0 +django-async-messages==0.3.1 +django-braces==1.13.0 +django-classy-tags==0.8.0 +django-cookie-law==2.0.1 +django-cors-headers==2.5.2 +django-countries==5.3.3 +django-datetime-widget==0.9.3 +django-debug-toolbar==2.0 +django-extensions==2.1.6 +django-htmlmin==0.11.0 +django-leaflet==0.24.0 +django-mailbox==4.8.0 +django-oauth-toolkit==1.2.0 +django-oauth2-provider==0.2.6.1 +django-redis==4.10.0 +django-rest-framework==0.1.0 +django-rest-swagger==2.2.0 +django-rq==1.3.1 +django-rq-dashboard==0.3.3 +django-ses==0.8.10 +django-shell-plus==1.1.7 +django-social-share==1.3.2 +django-suit==0.2.26 +django-suit-rq==1.0.1 +django-tz-detect==0.2.9 +djangorestframework==3.9.2 docopt==0.6.2 +docutils==0.14 entrypoints==0.3 -gufw==19.10.0 +execnet==1.5.0 +factory-boy==2.11.1 +Faker==1.0.4 +fastparquet==0.3.2 +fitparse==1.1.0 +Flask==1.0.2 +fsspec==0.5.2 +future==0.17.1 +geocoder==1.38.1 +geos==0.2.1 +grpcio==1.26.0 +grpcio-tools==1.26.0 +holoviews==1.11.3 html5lib==1.0.1 htmlmin==0.1.12 -httplib2==0.14.0 +HTMLParser==0.0.2 +httplib2==0.12.1 +hvplot==0.4.0 +icalendar==4.0.3 idna==2.8 -importlib-metadata==1.4.0 -ipykernel==5.1.3 -ipython==7.11.1 -ipython-genutils==0.1.0 -ipywidgets==7.5.1 -Jade-Application-Kit==2.1.3 -jedi==0.15.1 -Jinja2==2.10.3 -jsonschema==3.2.0 -jupyter-client==5.3.4 +image==1.5.27 +importlib-resources==1.0.2 +ipykernel==5.1.0 +ipython==7.4.0 +ipython-genutils==0.2.0 +ipywidgets==7.4.2 +iso8601==0.1.12 +isodate==0.6.0 +itsdangerous==1.1.0 +itypes==1.1.0 +jedi==0.13.3 +jeepney==0.4 +Jinja2==2.10 +json5==0.8.5 +jsonschema==3.0.1 +jupyter==1.0.0 +jupyter-client==5.2.4 jupyter-console==6.0.0 -jupyter-core==4.6.1 -keyutils==0.6 -kiwisolver==1.1.0 -kombu==4.6.7 -lensfun==0.3.2 -LibAppArmor==2.13.3 -lightdm-gtk-greeter-settings==1.2.2 -lockfile==0.12.2 -louis==3.12.0 -lxml==4.4.2 +jupyter-core==4.4.0 +jupyterlab==0.35.4 +jupyterlab-server==0.3.0 +keyring==18.0.0 +kiwisolver==1.0.1 +kombu==4.5.0 +llvmlite==0.30.0 +lxml==4.3.2 +Markdown==3.0.1 MarkupSafe==1.1.1 -matplotlib==3.1.2 -menulibre==2.2.1 +matplotlib==3.0.3 +minify==0.1.4 +MiniMockTest==0.5 mistune==0.8.4 -more-itertools==8.1.0 -msgpack==0.6.2 -mugshot==0.4.2 -nbconvert==5.6.1 +mock==2.0.0 +more-itertools==6.0.0 +mpld3==0.3 +mysqlclient==1.4.2.post1 +nbconvert==5.4.1 nbformat==4.4.0 +newrelic==5.2.1.129 nose==1.3.7 -notebook==6.0.2 -npyscreen==4.10.5 -numpy==1.18.1 -openshot-qt==2.4.4 -ordered-set==3.1.1 -packaging==20.0 -pacman-mirrors==4.16.2 +nose-parameterized==0.6.0 +notebook==5.7.6 +numba==0.46.0 +numpy==1.16.2 +oauth2==1.9.0.post1 +oauthlib==3.0.1 +openapi-codec==1.3.2 +packaging==19.0 +pandas==0.24.2 pandocfilters==1.4.2 -parso==0.5.2 -pep517==0.6.0 -pexpect==4.7.0 +param==1.8.2 +parso==0.3.4 +pathspec==0.5.9 +pbr==5.1.3 +pexpect==4.6.0 pickleshare==0.7.5 -Pillow==6.2.1 -pkgconfig==1.5.1 -pkginfo==1.5.0.1 -ply==3.11 -progress==1.5 -prometheus-client==0.7.1 -prompt-toolkit==3.0.2 -psutil==5.6.7 +Pillow==5.4.1 +pip-upgrader==1.4.6 +pluggy==0.9.0 +prometheus-client==0.6.0 +prompt-toolkit==2.0.9 +protobuf==3.11.1 +psycopg2==2.8.1 ptyprocess==0.6.0 -pycairo==1.18.2 +py==1.8.0 +pyarrow==0.15.0 +pycairo==1.19.0 pycparser==2.19 -pycups==1.9.74 -pycurl==7.43.0.3 -Pygments==2.5.2 -PyGObject==3.34.0 -pymongo==3.10.1 -pyOpenSSL==19.1.0 -pyparsing==2.4.6 -PyQt5==5.14.1 -PyQt5-sip==12.7.0 -PyQtWebEngine==5.14.0 -Pyro4==4.77 -pyrsistent==0.15.7 -python-dateutil==2.8.1 -python-distutils-extra==2.39 -pytoml==0.1.21 -pytz==2019.3 -pyxdg==0.26 -PyYAML==5.3 -pyzmq==18.1.1 -rados==2.0.0 -rbd==2.0.0 -redis==3.3.11 -requests==2.22.0 -retrying==1.3.3 -rgw==2.0.0 -scipy==1.4.1 +Pygments==2.3.1 +pyparsing==2.3.1 +pyrsistent==0.14.11 +pyshp==2.1.0 +pytest==4.3.1 +pytest-django==3.4.8 +pytest-forked==1.0.2 +pytest-runner==4.4 +pytest-sugar==0.9.2 +pytest-xdist==1.27.0 +python-dateutil==2.8.0 +python-memcached==1.59 +python-twitter==3.5 +pytz==2018.9 +pyviz-comms==0.7.1 +pywin32-ctypes==0.2.0 +pywinpty==0.5.5 +PyYAML==5.1 +pyzmq==18.0.1 +qtconsole==4.4.3 +ratelim==0.1.6 +redis==3.2.1 +requests==2.21.0 +requests-oauthlib==1.2.0 +rowingdata==2.6.6 +rowingphysics==0.5.0 +rq==0.13.0 +rules==2.1 +scipy==1.2.1 +SecretStorage==3.1.1 Send2Trash==1.5.0 -serpent==1.27 -six==1.13.0 -soupsieve==1.9.5 -SQLAlchemy==1.3.12 -team==1.0 -terminado==0.8.3 -testpath==0.4.4 -tornado==6.0.3 -traitlets==4.3.3 -udiskie==2.0.2 -ufw==0.36 -urllib3==1.25.7 +shell==1.0.1 +shortuuid==0.5.0 +simplejson==3.16.0 +six==1.12.0 +soupsieve==1.8 +SQLAlchemy==1.3.1 +sqlparse==0.3.0 +stravalib==0.10.2 +termcolor==1.1.0 +terminado==0.8.1 +terminaltables==3.1.0 +testpath==0.4.2 +text-unidecode==1.2 +thrift==0.11.0 +timezonefinder==4.0.1 +tk==0.1.0 +toolz==0.10.0 +tornado==6.0.1 +tqdm==4.31.1 +traitlets==4.3.2 +units==0.7 +uritemplate==3.0.0 +urllib3==1.24.1 +VerbalExpressions==0.0.2 vine==1.3.0 -virtualenv==16.1.0 -wcwidth==0.1.8 +wcwidth==0.1.7 webencodings==0.5.1 -widgetsnbextension==3.5.1 -wrapt==1.11.2 +Werkzeug==0.15.1 +widgetsnbextension==3.4.2 xlrd==1.2.0 -zipp==0.6.0 +xmltodict==0.12.0 +yamjam==0.1.7 +yamllint==1.15.0 +yuicompressor==2.4.8 diff --git a/rowers/tests/mocks.py b/rowers/tests/mocks.py index d725b8b5..30e0d69b 100644 --- a/rowers/tests/mocks.py +++ b/rowers/tests/mocks.py @@ -610,6 +610,7 @@ def mocked_requests(*args, **kwargs): def __init__(self, json_data, status_code): self.json_data = json_data self.status_code = status_code + self.ok = True def json(self): return self.json_data diff --git a/rowers/tests/test_aworkouts.py b/rowers/tests/test_aworkouts.py index d82bc9ac..55d48c5d 100644 --- a/rowers/tests/test_aworkouts.py +++ b/rowers/tests/test_aworkouts.py @@ -242,9 +242,9 @@ class WorkoutViewTest(TestCase): @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.getsmallrowdata_db') - # @patch('rowers.weather.requests.get', side_effect=mocked_requests) + @patch('rowers.weather.requests.get', side_effect=mocked_requests) def test_waterworkout_view(self, - mocked_sqlalchemy, mocked_getsmallrowdata_db): + mocked_sqlalchemy, mocked_getsmallrowdata_db, mocked_requests): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index ffaf1658..543170d5 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -777,7 +777,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_favoritecharts_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_favoritecharts_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) @@ -788,7 +788,7 @@ class PermissionsViewTests(TestCase): login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) - url = reverse('rower_favoritecharts_view',kwargs={'id':self.ubasic.id}) + url = reverse('rower_favoritecharts_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) self.assertEqual(response.status_code,403) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index ab64ca8b..783648c6 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -2214,8 +2214,21 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): 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 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' From e51514142937932023bb6354ea06c0bf41b40d08 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 8 Feb 2020 10:20:32 +0100 Subject: [PATCH 80/80] checking in changes --- requirements.txt | 2 +- rowers/interactiveplots.py | 10 ++++++++-- rowers/views/exportviews.py | 7 +++++-- rowers/views/planviews.py | 2 -- rowers/views/statements.py | 10 ++++++++-- rowers/views/workoutviews.py | 29 +++++++++++++++++++---------- 6 files changed, 41 insertions(+), 19 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8ebc3c9b..8f8eb580 100644 --- a/requirements.txt +++ b/requirements.txt @@ -174,7 +174,7 @@ ratelim==0.1.6 redis==3.2.1 requests==2.21.0 requests-oauthlib==1.2.0 -rowingdata==2.6.6 +rowingdata==2.6.7 rowingphysics==0.5.0 rq==0.13.0 rules==2.1 diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 428c1968..73c72151 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -2680,7 +2680,10 @@ def interactive_windchart(id=0,promember=0): if rowdata == 0: return 0 - dist = rowdata.df.loc[:,'cum_dist'] + try: + dist = rowdata.df.loc[:,'cum_dist'] + except KeyError: + return ['','No Data Found'] try: vwind = rowdata.df.loc[:,'vwind'] @@ -2770,7 +2773,10 @@ def interactive_streamchart(id=0,promember=0): if rowdata == 0: return "","No Valid Data Available" - dist = rowdata.df.loc[:,'cum_dist'] + try: + dist = rowdata.df.loc[:,'cum_dist'] + except KeyError: + return ['','No Data found'] try: vstream = rowdata.df.loc[:,'vstream'] diff --git a/rowers/views/exportviews.py b/rowers/views/exportviews.py index b415696a..1b6e0e85 100644 --- a/rowers/views/exportviews.py +++ b/rowers/views/exportviews.py @@ -249,8 +249,11 @@ 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 diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index 034a1ba9..f4ff5fbc 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1224,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) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index c011da93..3bdac2cb 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -347,6 +347,9 @@ def get_user_by_userid(*args,**kwargs): 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 @@ -370,6 +373,9 @@ 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'] @@ -977,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 diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 783648c6..5c6cb869 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -2944,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 @@ -2970,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) @@ -2987,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: @@ -4068,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: