Private
Public Access
1
0
Files
rowsandall/rowers/rower_rules.py

855 lines
24 KiB
Python

import rules
import datetime
from django.utils import timezone
# PERMISSIONS
# USER permissions
"""
USER permissions
- There are 5 types of user: basic, pro, plan, coach, freecoach
- These methods exist on user
- user.rower
- user.is_anonymous
- user.is_staff
- These methods exist on rower
- rower.rowerplan
- rower.protrialexpires
- rower.plantrialexpires
- rower.mycoachgroup
- rower.coachinggroups
- rower.team
- These Rules apply to Rowers
- Coach can have any number of groups
- test_permissions.PermissionFreeCoach.test_coach_groupmanager
- test_permissions.PermissionsBasicsTests.test_coach_groupmanager
- test_permissions.PermissionsViewTests.test_coach_groups_create
- Pro and Plan users can have one group and not more
- test_permissions.PermissionsFreeCoach.test_pro_groupmanager
- test_permissions.PermissionsViewTests.test_pro_groups_create
- Free Coach user cannot have workouts
- test_permissions.PermissionFreeCoach.test_add_workout_freecoach
- Free coach can create more than one group
- test_permissions.PermissionsFreeCoach.test_plan_groupmanager
- Free Coach & Plan (Self Coach) can create planned sessions and team planned sessions
- test_permissions.PermissionsFreeCoach.test_plan_create_session
- test_permissions.PermissionsFreeCoach.test_coach_create_session
- Pro cannot create planned sessions or team planned sessions
- test_permissions.PermissionsViewTests.test_pro_create_session
- Basic cannot join groups led by Free Coach
- test_permissions.PermissionFreeCoach.test_add_basic_pro_or_plan
- Coach can edit athlete settings (if in coachinggroup)
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_prefs
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_prefs_not
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_settings
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_settings_not
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_account
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_account_not
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_exportsettings
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_exportsettings_not
- Coach can upload a workout on behalf of athlete
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload_not
- Pro and Plan cannot upload a workout on behalf of athlete
- test_permissions.PermissionsViewTests.test_plan_edit_athlete_upload
- Coach can run analytics for athlete
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_analysis
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_analysis_not
- test_permissions.PermissionsViewTests.test_pro_edit_athlete_analysis
- Pro and Plan user cannot run analysis for members of their groups
- test_permissions.PermissionsViewTests.test_plan_edit_athlete_analysis
- Pro and Plan user cannot edit athlete sessings
- test_permissions.PermissionsViewTests.test_plan_edit_athlete_settings
- test_permissions.PermissionsViewTests.test_pro_edit_athlete_settings
- Self-Coach and Pro cannot create more than one group
- test_permissions.PermissionsFreeCoach.test_plan_groupmanager
- Pro users and higher can join group led by other Pro or higher user
- test_permissions.PermissionsFreeCoach.test_add_proplan_pro_or_plan
- Pro or basic cannot create planned sessions or team planned sessions
- test_permissions.PermissionsFreeCoach.test_pro_create_plannedsession
"""
# used in can_plan_user
@rules.predicate
def user_is_not_basic(user):
if user.rower.rowerplan != 'basic':
return True
if user.rower.protrialexpires >= timezone.now().date():
return True # pragma: no cover
if user.rower.plantrialexpires >= timezone.now().date():
return True # pragma: no cover
if user.rower.coachtrialexpires >= timezone.now().date():
return True # pragma: no cover
return False
@rules.predicate
def user_is_coachee(user):
coaches = user.rower.get_coaches()
for coach in coaches:
if coach.rowerplan == 'coach':
return True
if coach.coachtrialexpires >= timezone.now().date():
return True
return False
@rules.predicate
def user_is_basic(user):
return not user_is_not_basic(user)
@rules.predicate
def can_start_trial(user):
if user.is_anonymous: # pragma: no cover
return False
return user.rower.protrialexpires == datetime.date(1970, 1, 1)
@rules.predicate
def can_start_plantrial(user):
if user.is_anonymous: # pragma: no cover
return False
return user.rower.plantrialexpires == 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)
@rules.predicate
def is_staff(user): # pragma: no cover
return user.is_staff
@rules.predicate
def is_coach(user):
if user is None:
return False
if user.is_anonymous:
return False
if user.rower.coachtrialexpires >= timezone.now().date():
return True
return user.rower.rowerplan in ['coach', 'freecoach']
@rules.predicate
def is_not_freecoach(user):
return user.rower.rowerplan != 'freecoach'
def is_paid_coach(user):
if user.rower.coachtrialexpires >= timezone.now().date():
return True
return user.rower.rowerplan == 'coach'
@rules.predicate
def is_planmember(user):
try:
r = user.rower
except AttributeError: # pragma: no cover
return False
if user.rower.coachtrialexpires >= timezone.now().date():
return True
return r.rowerplan in ['coach', 'plan'] # freecoach?
@rules.predicate
def is_promember(user):
try:
r = user.rower
except AttributeError:
return False
return r.rowerplan in ['pro', 'coach', 'plan'] # freecoach?
@rules.predicate
def is_protrial(user):
try:
r = user.rower
except AttributeError:
return False
if r.rowerplan == 'basic':
return r.protrialexpires >= timezone.now().date()
if r.rowerplan == 'freecoach':
if r.mycoachgroup is not None:
return len(r.mycoachgroup) >= 4
return False # pragma: no cover
@rules.predicate
def is_coachtrial(user):
try:
r = user.rower
except AttributeError:
return False
if r.rowerplan == 'basic':
return r.coachtrialexpires >= timezone.now().date()
return False # pragma: no cover
ispromember = is_promember | is_protrial | is_coach | is_coachtrial
can_have_teams = ispromember | is_coach | is_coachtrial
@rules.predicate
def can_add_team(user):
if is_coach(user):
return True
if ispromember(user) or is_planmember(user):
otherteams = user.rower.get_managed_teams()
if otherteams.count() == 0:
return True
if is_coachtrial(user):
return True
return False
@rules.predicate
def can_add_plan(user):
return isplanmember(user) or is_coach(user)
@rules.predicate
def can_add_workout(user):
if user.is_anonymous: # pragma: no cover
return False
return user.rower.rowerplan != 'freecoach'
@rules.predicate
def is_plantrial(user):
try:
r = user.rower
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
return False # pragma: no cover
isplanmember = is_planmember | is_plantrial
iscoachmember = is_coachtrial | is_coach
@rules.predicate
def can_add_session(user):
return isplanmember(user) or is_coach(user) or is_coachtrial(user)
# User / Coach relationships (Rower object)
@rules.predicate
def can_plan(user):
if user.is_anonymous: # pragma: no cover
return False
if user.rower.rowerplan in ['plan', 'coach']:
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
# checks if rower is coach of user (or is user himself)
@rules.predicate
def is_coach_user(usercoach, userrower):
if usercoach == userrower:
return True
if not is_coach(usercoach):
return False
r = userrower.rower
coaches = []
for group in r.coachinggroups.all():
newcoaches = group.get_coaches()
for coach in newcoaches:
coaches.append(coach)
for coach in coaches:
if usercoach.rower == coach:
return True
return False
# checks if rower is coach of user (or is user himself)
@rules.predicate
def is_anonymous_or_coach(usercoach, userrower): # pragma: no cover
if usercoach == userrower:
return True
if userrower.is_anonymous and userrower.is_anonymous:
return True
if not is_coach(usercoach):
return False
r = userrower.rower
coaches = []
for group in r.coachinggroups.all():
newcoaches = group.get_coaches()
for coach in newcoaches:
coaches.append(coach)
for coach in coaches:
if usercoach.rower == coach:
return True
return False
# check if rower and user are members of the same team
@rules.predicate
def is_rower_team_member(user, rower):
if user.rower == rower: # pragma: no cover
return True
if is_coach_user(user, rower.user):
return True
teams = rower.team.all()
for team in teams:
if team.private == 'open':
if team in user.rower.team.all():
return True
if team.manager == user:
return True
return False
@rules.predicate
def can_add_workout_member(user, rower):
if not user: # pragma: no cover
return False
if user.is_anonymous: # pragma: no cover
return False
if user == rower.user: # pragma: no cover
return True
# only below tested - need test user == rower.user
return is_coach(user) and user.rower in rower.get_coaches()
# check if user can plan for the rower
@rules.predicate
def can_plan_user(user, rower):
# user must have planning permission
if not can_plan(user):
return False
# if has planning permission, can always plan for himself
if rower == user.rower:
return True
teams = user.rower.get_managed_teams()
# free coach, plan etc cannot plan for basic
if not is_paid_coach(user) and user_is_not_basic(user):
for t in teams:
if rower in t.rower.all(): # pragma: no cover
return True
# paying coach can plan for all kinds of rowers
if is_paid_coach(user):
for t in teams:
if rower in t.rower.all():
return True
return False
rules.add_perm('rower.add_plan', can_plan_user) # replaces checkaccessplanuser
rules.add_perm('rower.is_coach', is_coach_user) # replaces checkaccessuser
rules.add_perm('rower.is_pro', ispromember)
rules.add_perm('rower.is_staff', is_staff)
rules.add_perm('rower.is_not_freecoach', is_not_freecoach)
# WORKOUT permissions
"""
WORKOUT permissions
- These methods exist on Workout
- workout.privacy
- These rules apply to workouts
- User can add, delete and change their own workouts
- test_aworkouts
- Coach can add and change workouts for their athletes, but not delete
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload_not
- Basic, Pro and Plan user can view but cannot add or change an athlete's workout
- test_permissions.PermissionsViewTests.test_coach_edit_athlete_workout
- test_permissions.PermissionsViewTests.test_plan_edit_athlete_upload
- test_permissions.PermissionsViewTests.test_plan_edit_athlete_workout
- test_permissions.PermissionsViewTests.test_basic_edit_athelte_workout
- Anonymous users can view team members' workout (but not see list of workouts)
- test_aworkouts
- Rules for Workouts are transferred to objects related to the Workout,
- Charts
- Video Analysis
- WorkoutComment?
"""
# check if user is owner or coach of owner of workout
@rules.predicate
def is_workout_user(user, workout):
if user.is_anonymous:
return False
try:
r = user.rower
except AttributeError: # pragma: no cover
return False
if workout.user == r:
return True
return is_coach_user(user, workout.user.user)
# check if user is in same team as owner of workout
@rules.predicate
def is_workout_team(user, workout):
if user.is_anonymous: # pragma: no cover
return False
try:
r = user.rower
except AttributeError: # pragma: no cover
return False
if workout.user == r:
return True
return is_rower_team_member(user, workout.user)
# check if user can see workout
@rules.predicate
def can_view_workout(user, workout):
if workout.workoutsource == 'strava':
return user == workout.user.user
if workout.privacy not in ('hidden', 'private'):
return True
if user.is_anonymous: # pragma: no cover
return False
return user == workout.user.user # pragma: no cover
can_change_workout = is_workout_user
# replaces checkworkoutuser
rules.add_perm('workout.change_workout', can_change_workout)
# replaces checkworkoutuserview
rules.add_perm('workout.view_workout', can_view_workout)
# checkviewworkouts
# PLANNING permissions
"""
- These rules apply to planning
- Free coach can create planned sessions and team planned sessions
- test_permissions.PermissionsViewTests.test_coach_create_session
- Self coach and higher can create planned sessions and team planned sessions
- test_permissions.PermissionsFreeCoach.test_plan_create_session
- test_permissions.PermissionsFreeCoach.test_coach_create_session
- Coach can create planned sessions and team planned sessions
- test_permissions.PermissionsFreeCoach.test_plan_create_session
- test_permissions.PermissionsFreeCoach.test_coach_create_session
- Self Coach and higher can create planned sessions and team planned sessions
- test_permissions.PermissionsViewTests.test_plan_create_session
- Pro or Basic cannot create planned sessions or team planned sessions
- test_permissions.PermissionsFreeCoach.test_pro_create_plannedsession
- test_permissions.PermissionsFreeCoach.test_basic_create_plannedsession
- WHO can comment (plannedsession_comment_view)
- Only Session manager can change session
- Strict View rules (stricter than workouts)
- Basic user cannot manage a training plan
- TrainingTarget
- rules for view, add, change, delete
- TrainingPlan
- rules for view, add, change, delete
- Cycle
- inherits rules from TrainingPlan
- PlannedSession
- rules for view, add, change, delete, clone, save as template
- check team
- Special rules for Race (cannot be edited ex post)
"""
# Training Target rules
# untested can_view_target to can_delete_target
@rules.predicate
def can_view_target(user, target): # pragma: no cover
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): # pragma: no cover
if user.is_anonymous:
return False
return user == target.manager.user
@rules.predicate
def can_delete_target(user, target):
if user.is_anonymous: # pragma: no cover
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: # pragma: no cover
return False
if user == plan.manager.user:
return True
# a plan's coach can view as well
# below untested
if is_coach_user(user, plan.manager.user): # pragma: no cover
return True
# the object can view as well
if user.rower in plan.rowers.all(): # pragma: no cover
return True
@rules.predicate
def can_change_plan(user, plan):
if user.is_anonymous: # pragma: no cover
return False
return user == plan.manager.user # pragma: no cover
# below untested
@rules.predicate
def can_delete_plan(user, plan):
if user.is_anonymous: # pragma: no cover
return False
return user == plan.manager.user
rules.add_perm('plan.view_plan', can_view_plan)
rules.add_perm('plan.change_plan', can_change_plan)
rules.add_perm('plan.delete_plan', can_delete_plan)
rules.add_perm('plan.can_add_plan', can_add_plan)
# untested
@rules.predicate
def can_view_cycle(user, cycle): # pragma: no cover
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): # pragma: no cover
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): # pragma: no cover
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']: # pragma: no cover
return True
if user.is_anonymous: # pragma: no cover
return False
# session manager can view session
if user == session.manager:
return True
# if you're a rower in the session you can view it
# below untested
if user.rower in session.rower.all(): # pragma: no cover
return True
# coach users can view sessions created by their team members
# below untested
if is_coach(user) or is_coachtrial(user): # pragma: no cover
teams = user.rower.get_managed_teams()
for t in teams:
teamusers = [member.u for member in t.rower.all()]
if session.manager in teamusers:
return True
return False # pragma: no cover
@rules.predicate
def can_change_session(user, session):
if user.is_anonymous: # pragma: no cover
return False
# session part of a race should not be changed through the session interface
if session.sessiontype in ['race', 'indoorrace']: # pragma: no cover
return False
if user == session.manager:
return True
return False # pragma: no cover
@rules.predicate
def can_delete_session(user, session): # pragma: no cover
if user.is_anonymous:
return False
if session.sessiontype in ['race', 'indoorrace']:
return False
if user == session.manager:
return True
return False
rules.add_perm('plannedsession.add_session', can_add_session)
rules.add_perm('plannedsession.view_session', can_view_session)
rules.add_perm('plannedsession.change_session', can_change_session)
rules.add_perm('plannedsession.delete_session', can_delete_session)
# TEAM (group) permissions
"""
- These methods exist on team
- team.manager
- team.private
- These rules apply to a team
- A Pro or Plan rower can be manager of only 1 team
- test_permissions.PermissionsFreeCoach.test_pro_groupmanager
- test_permissions.PermissionsViewTests.test_pro_groups_create
- A coach can have any number of teams (as manager)
- test_permissions.PermissionsBasicTest.test_coach_groupmanager
- Basic user cannot manage a group
- test_permissions.PermissionsFreeCoach.test_basic_groupmanager
- Basic user cannot join groups led by Free Coach or Pro
- test_permissions.PermissionFreeCoach.test_add_basic_pro_or_plan
- Basic athletes can be member of Coach led group
- test_permissions.PermissionsBasicTest.test_add_coach
- Pro users (and higher) can join team led by other Pro (or higher) user
- test_permissions.PermissionsFreeCoach.test_add_proplan_pro_or_plan
- test_permissions.PermissionsViewTests.test_team_member_request_pro_pro
- test_permissions.PermissionsViewTests.test_team_member_request_basic_pro
- On downgrade, Coach users lose all but their oldest team (add test!)
"""
# check if user is manager of the team
@rules.predicate
def is_team_manager(user, team):
return team.manager == user
# check is user is member of team - untested
@rules.predicate
def is_team_member(user, team): # pragma: no cover
members = team.rower.all()
return user in [member.user for member in members]
# check if user can view team
@rules.predicate
def can_view_team(user, team):
# user based - below untested
if team.manager.rower.coachtrialexpires >= timezone.now().date():
return True
if user.rower.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach': # pragma: no cover
return is_plantrial(user) or is_protrial(user) or is_coachtrial(user)
# team is public
if team.private == 'open':
return True
# team is private - below untested
return is_team_member(user, team) | is_team_manager(user, team) # pragma: no cover
@rules.predicate
def can_change_team(user, team):
return is_team_manager(user, team)
@rules.predicate
def can_delete_team(user, team):
return is_team_manager(user, team)
@rules.predicate
def can_join_team(user, team):
return is_paid_coach(team.manager) or ispromember(user)
# For Team functionality
rules.add_perm('teams.view_team', can_view_team)
rules.add_perm('teams.add_team', user_is_not_basic)
rules.add_perm('teams.change_team', can_change_team)
rules.add_perm('teams.delete_team', can_delete_team)
# RACING permissions
"""
- VirtualRace
- rules to add, view, delete, change
- GeoCourse
- rules to add, view, delete, change
- RaceLogo
"""
@rules.predicate
def can_change_course(user, course):
if user.is_anonymous: # pragma: no cover
return False
return course.manager == user.rower
# untested
@rules.predicate
def can_delete_course(user, course):
if user.is_anonymous: # pragma: no cover
return False
return course.manager == user.rower
@rules.predicate
def can_delete_logo(user, logo):
if user.is_anonymous: # pragma: no cover
return False
return logo.user == user # pragma: no cover
@rules.predicate
def can_change_race(user, race):
if user.is_anonymous: # pragma: no cover
return False
return race.manager == user
# everybody can view or add a course
rules.add_perm('course.change_course', can_change_course)
rules.add_perm('course.delete_course', can_delete_course)
rules.add_perm('racelogo.delete_logo', can_delete_logo)
# everybody can view a race
rules.add_perm('virtualevent.change_race', can_change_race)
# can races be deleted?
# ANALYSIS permissions
"""
- Conditions
- Alerts
- rules to add, view, delete, change
- cpdata
"""