diff --git a/rowers/models.py b/rowers/models.py index 4ea37a23..80c83d27 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -862,9 +862,6 @@ weightcategories = mytypes.weightcategories plans = ( ('basic', 'basic'), ('pro', 'pro'), - ('plan', 'plan'), - ('coach', 'coach'), - ('freecoach', 'freecoach'), ) paymenttypes = ( @@ -1365,7 +1362,7 @@ class Rower(models.Model): @property def ispaid(self): # pragma: no cover - return self.rowerplan in ['pro', 'plan', 'coach'] + return self.rowerplan == 'pro' class DeactivateUserForm(forms.ModelForm): @@ -2980,7 +2977,7 @@ class PlannedSession(models.Model): if not is_virtualevent: if not can_add_session(self.manager): raise ValidationError( - "You must be a Self-Coach user or higher to create a planned session" + "You must be a Pro user or higher to create a planned session" ) # interval string diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index b7bb4164..df24bfa1 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -17,31 +17,31 @@ USER permissions - These methods exist on rower - rower.rowerplan - rower.protrialexpires - - rower.plantrialexpires + - rower.protrialexpires - rower.mycoachgroup - rower.coachinggroups - rower.team - These Rules apply to Rowers - - Coach can have any number of groups + - Pro 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 + - (REMOVE) 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 + - (REMOVE) Free Coach user cannot have workouts - test_permissions.PermissionFreeCoach.test_add_workout_freecoach - - Free coach can create more than one group + - (REMOVE) 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 + - (ADJUST) 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 + - (REMOVE) Pro cannot create planned sessions or team planned sessions - test_permissions.PermissionsViewTests.test_pro_create_session - - Basic cannot join groups led by Free Coach + - (REMOVE) 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) + - (ADJUST) 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 @@ -50,25 +50,25 @@ USER permissions - 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 + - (ADJUST) 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 + - (REMOVE) 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 + - (ADJUST) 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 + - (ADJUST) 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 + - (ADJUST) 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 + - (REMOVE) 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 + - (ADJSUT) Pro or basic cannot create planned sessions or team planned sessions - test_permissions.PermissionsFreeCoach.test_pro_create_plannedsession """ @@ -84,7 +84,7 @@ def user_is_not_basic(user): if user.rower.protrialexpires >= timezone.now().date(): return True # pragma: no cover - if user.rower.plantrialexpires >= timezone.now().date(): + if user.rower.protrialexpires >= timezone.now().date(): return True # pragma: no cover if user.rower.coachtrialexpires >= timezone.now().date(): @@ -97,9 +97,9 @@ def user_is_not_basic(user): def user_is_coachee(user): coaches = user.rower.get_coaches() for coach in coaches: - if coach.rowerplan == 'coach': + if coach.rowerplan == 'pro': return True - if coach.coachtrialexpires >= timezone.now().date(): + if coach.protrialexpires >= timezone.now().date(): return True return False @@ -123,14 +123,14 @@ def can_start_plantrial(user): if user.is_anonymous: # pragma: no cover return False - return user.rower.plantrialexpires == datetime.date(1970, 1, 1) + return user.rower.protrialexpires == datetime.date(1970, 1, 1) @rules.predicate def can_start_coachtrial(user): if user.is_anonymous: # pragma: no cover return False - return user.rower.coachtrialexpires == datetime.date(1970, 1, 1) + return user.rower.protrialexpires == datetime.date(1970, 1, 1) @rules.predicate @@ -144,20 +144,16 @@ def is_coach(user): return False if user.is_anonymous: return False - if user.rower.coachtrialexpires >= timezone.now().date(): + if user.rower.protrialexpires >= timezone.now().date(): return True - return user.rower.rowerplan in ['coach', 'freecoach'] + return user.rower.rowerplan == 'pro' -@rules.predicate -def is_not_freecoach(user): - return user.rower.rowerplan != 'freecoach' - def is_paid_coach(user): - if user.rower.coachtrialexpires >= timezone.now().date(): + if user.rower.protrialexpires >= timezone.now().date(): return True - return user.rower.rowerplan == 'coach' + return user.rower.rowerplan == 'pro' @rules.predicate @@ -167,10 +163,10 @@ def is_planmember(user): except AttributeError: # pragma: no cover return False - if user.rower.coachtrialexpires >= timezone.now().date(): + if user.rower.protrialexpires >= timezone.now().date(): return True - return r.rowerplan in ['coach', 'plan'] # freecoach? + return r.rowerplan == 'pro' @rules.predicate @@ -180,7 +176,7 @@ def is_promember(user): except AttributeError: return False - return r.rowerplan in ['pro', 'coach', 'plan'] # freecoach? + return r.rowerplan == 'pro' @rules.predicate @@ -192,9 +188,6 @@ def is_protrial(user): if r.rowerplan == 'basic': return r.protrialexpires >= timezone.now().date() - if r.rowerplan == 'freecoach': - if r.mycoachgroup is not None: - return len(r.mycoachgroup) >= 4 return False # pragma: no cover @@ -206,7 +199,7 @@ def is_coachtrial(user): return False if r.rowerplan == 'basic': - return r.coachtrialexpires >= timezone.now().date() + return r.protrialexpires >= timezone.now().date() return False # pragma: no cover @@ -235,7 +228,7 @@ def can_add_team(user): @rules.predicate def can_add_plan(user): - return isplanmember(user) or is_coach(user) + return ispromember(user) @rules.predicate @@ -243,7 +236,7 @@ def can_add_workout(user): if user.is_anonymous: # pragma: no cover return False - return user.rower.rowerplan != 'freecoach' + return True @rules.predicate @@ -253,11 +246,8 @@ def is_plantrial(user): except AttributeError: # pragma: no cover return False - if r.rowerplan in ['basic', 'pro']: - return r.plantrialexpires >= timezone.now().date() - if r.rowerplan == 'freecoach': - if r.mycoachgroup is not None: - return len(r.mycoachgroup) >= 4 + if r.rowerplan == 'basic': + return r.protrialexpires >= timezone.now().date() return False # pragma: no cover @@ -278,13 +268,10 @@ def can_add_session(user): def can_plan(user): if user.is_anonymous: # pragma: no cover return False - if user.rower.rowerplan in ['plan', 'coach']: + if user.rower.rowerplan == 'pro': return True - if user.rower.rowerplan in ['basic', 'pro']: - return user.rower.plantrialexpires >= timezone.now().date() or user.rower.coachtrialexpires >= timezone.now().date() - if user.rower.rowerplan == 'freecoach': # pragma: no cover - if user.rower.mycoachgroup is not None: - return len(user.rower.mycoachgroup) >= 4 + if user.rower.rowerplan == 'basic': + return user.rower.protrialexpires >= timezone.now().date() or user.rower.protrialexpires >= timezone.now().date() # checks if rower is coach of user (or is user himself) @@ -408,7 +395,7 @@ 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) -rules.add_perm('rower.is_not_freecoach', is_not_freecoach) + # WORKOUT permissions @@ -781,9 +768,9 @@ def is_team_member(user, team): # pragma: no cover @rules.predicate def can_view_team(user, team): # user based - below untested - if team.manager.rower.coachtrialexpires >= timezone.now().date(): + if team.manager.rower.protrialexpires >= timezone.now().date(): return True - if user.rower.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach': # pragma: no cover + if user.rower.rowerplan == 'basic' and team.manager.rower.rowerplan != 'pro': # pragma: no cover return is_plantrial(user) or is_protrial(user) or is_coachtrial(user) # team is public if team.private == 'open': diff --git a/rowers/tests/test_permissions2.py b/rowers/tests/test_permissions2.py index 1675c7df..bb205602 100644 --- a/rowers/tests/test_permissions2.py +++ b/rowers/tests/test_permissions2.py @@ -49,7 +49,7 @@ for id, row in df.iterrows(): viewstotest.append(tpl) -plans = ['basic','plan','coach','pro'] +plans = ['basic','pro'] @override_settings(TESTING=True) class PermissionsViewTests(TestCase): @@ -61,7 +61,7 @@ class PermissionsViewTests(TestCase): rcoach = Rower.objects.create(user=ucoach, birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True,gdproptindate=timezone.now(), - rowerplan='coach',clubsize=10) + rowerplan='pro',clubsize=10) ucoach_workouts = WorkoutFactory.create_batch(5, user=rcoach) coachinggroup = CoachingGroup.objects.create() @@ -83,7 +83,7 @@ class PermissionsViewTests(TestCase): rplan = Rower.objects.create(user=uplan, birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True,gdproptindate=timezone.now(), - rowerplan='plan',clubsize=10) + rowerplan='pro',clubsize=10) uplan_workouts = WorkoutFactory.create_batch(5, user=rplan) rcoach.save() @@ -103,7 +103,7 @@ class PermissionsViewTests(TestCase): rplan2 = Rower.objects.create(user=uplan2, birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True,gdproptindate=timezone.now(), - rowerplan='plan',clubsize=10) + rowerplan='pro',clubsize=10) uplan2_workouts = WorkoutFactory.create_batch(5, user=rplan2) rcoach.save() @@ -287,12 +287,6 @@ class PermissionsViewTests(TestCase): 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']: @@ -370,14 +364,6 @@ class PermissionsViewTests(TestCase): 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']: diff --git a/rowers/tests/test_plans.py b/rowers/tests/test_plans.py index 79d180f5..66e55be8 100644 --- a/rowers/tests/test_plans.py +++ b/rowers/tests/test_plans.py @@ -33,7 +33,7 @@ class TrainingPlanTest(TestCase): gdproptin=True, ftpset=True,surveydone=True, defaulttimezone='US/Pacific', gdproptindate=timezone.now(), - rowerplan='coach') + rowerplan='pro') self.c = Client() self.user_workouts = WorkoutFactory.create_batch(5, user=self.r) @@ -200,7 +200,7 @@ class SessionTemplateTest(TestCase): birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True, gdproptindate=timezone.now(), - rowerplan='coach') + rowerplan='pro') self.c = Client() self.user_workouts = WorkoutFactory.create_batch(5, user=self.r) @@ -306,7 +306,7 @@ class SessionLinkTest(TestCase): birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True, gdproptindate=timezone.now(), - rowerplan='coach') + rowerplan='pro') self.c = Client() self.user_workouts = WorkoutFactory.create_batch(5, user=self.r) @@ -551,7 +551,7 @@ class SessionCompleteTest(TestCase): birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True, gdproptindate=timezone.now(), - rowerplan='coach') + rowerplan='pro') self.c = Client() @@ -808,7 +808,7 @@ class ChallengeCompleteTest(TestCase): birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True, gdproptindate=timezone.now(), - rowerplan='coach') + rowerplan='pro') self.c = Client() @@ -1032,7 +1032,7 @@ class MandatoryTestCompleteTest(TestCase): birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True, gdproptindate=timezone.now(), - rowerplan='coach') + rowerplan='pro') self.c = Client() @@ -1246,7 +1246,7 @@ class PlannedSessionsView(TestCase): gdproptin=True, ftpset=True,surveydone=True, gdproptindate=timezone.now(), defaulttimezone='US/Pacific', - rowerplan='coach') + rowerplan='pro') self.r.save() self.c = Client() diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz index ec90a271..d32442bd 100644 Binary files a/rowers/tests/testdata/testdata.tcx.gz and b/rowers/tests/testdata/testdata.tcx.gz differ diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 0c19b5c5..914c5ef8 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -38,7 +38,7 @@ 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, +53,59,team_workout_upload_view,upload workout for team member,TRUE,302,pro,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, 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, @@ -159,12 +159,12 @@ 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,TRUE,TRUE, +204,265,rower_create_trainingplan,create training plan,TRUE,302,pro,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,FALSE,FALSE, +209,271,rower_trainingplan_view,see training plan,TRUE,200,pro,200,302,pro,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, @@ -175,11 +175,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,TRUE,TRUE, +220,292,plannedsession_teamcreate_view,create planned session for team,TRUE,302,pro,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,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, +222,298,plannedsession_create_view,create planned session ,TRUE,302,pro,200,302,FALSE,200,302,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +223,300,plannedsession_multiclone_view,clone multiple planned sessions,TRUE,302,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, +224,302,plannedsession_multicreate_view,create multiple planned sessions,TRUE,302,pro,200,302,pro,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, @@ -188,9 +188,9 @@ 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, +233,319,plannedsessions_manage_view,manage planned sesions,TRUE,302,pro,200,302,pro,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, +235,325,plannedsessions_coach_icsemail_view,team view,TRUE,302,pro,302,403,pro,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,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,