diff --git a/rowers/admin.py b/rowers/admin.py index fad8795b..7ef887fb 100644 --- a/rowers/admin.py +++ b/rowers/admin.py @@ -24,7 +24,7 @@ class RowerInline(admin.StackedInline): ('Billing Details', {'fields':('street_address','city','postal_code','country','paymentprocessor','customer_id')}), ('Rower Plan', - {'fields':('paidplan','rowerplan','paymenttype','planexpires','teamplanexpires','protrialexpires','plantrialexpires',)}), + {'fields':('paidplan','rowerplan','paymenttype','planexpires','teamplanexpires','protrialexpires','plantrialexpires','clubsize','offercoaching')}), ('Rower Settings', {'fields': ('gdproptin','gdproptindate','weightcategory','sex','adaptiveclass','birthdate','getemailnotifications', @@ -60,7 +60,7 @@ class RowerInline(admin.StackedInline): #class UserAdmin(UserAdmin): class UserAdmin(admin.ModelAdmin): inlines = (RowerInline,) - list_display = ('username','email','first_name','last_name','rowerplan') + list_display = ('username','email','first_name','last_name','rowerplan','clubsize') fieldsets = ( ('Personal info', @@ -75,6 +75,9 @@ class UserAdmin(admin.ModelAdmin): def rowerplan(self, obj): return obj.rower.rowerplan + def clubsize(self, obj): + return obj.rower.clubsize + class WorkoutAdmin(admin.ModelAdmin): list_display = ('date','user','name','workouttype','boattype') diff --git a/rowers/management/commands/processemail.py b/rowers/management/commands/processemail.py index 52022e06..7bf213f7 100644 --- a/rowers/management/commands/processemail.py +++ b/rowers/management/commands/processemail.py @@ -188,7 +188,7 @@ class Command(BaseCommand): res = polarstuff.get_all_new_workouts(polar_available) # Concept2 - rowers = Rower.objects.filter(c2_auto_import=True) + rowers = Rower.objects.filter(c2_auto_import=True).exclude(rowerplan='basic') for r in rowers: c2stuff.get_c2_workouts(r) @@ -287,7 +287,7 @@ class Command(BaseCommand): message.delete() # Strava - rowers = Rower.objects.filter(strava_auto_import=True) + rowers = Rower.objects.filter(strava_auto_import=True).exclude(rowerplan='basic') for r in rowers: stravastuff.get_strava_workouts(r) diff --git a/rowers/models.py b/rowers/models.py index 93dcfa58..ac7b00d1 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -1024,6 +1024,25 @@ def checkworkoutuser(user,workout): 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 diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py index 756cf751..c8a2e2a8 100644 --- a/rowers/plannedsessions.py +++ b/rowers/plannedsessions.py @@ -473,13 +473,14 @@ def remove_rower_session(r,ps): return 1 -def get_dates_timeperiod(request,startdatestring='',enddatestring=''): +def get_dates_timeperiod(request,startdatestring='',enddatestring='', + defaulttimeperiod='thisweek'): # set start end date according timeperiod timeperiod = request.GET.get('when') if not timeperiod: - timeperiod = 'thisweek' + timeperiod = defaulttimeperiod startdatestring = request.GET.get('startdate') enddatestring = request.GET.get('enddate') @@ -536,6 +537,10 @@ def get_dates_timeperiod(request,startdatestring='',enddatestring=''): enddate = startdate+timezone.timedelta(days=32) enddate = enddate.replace(day=1) enddate = enddate-timezone.timedelta(days=1) + elif timeperiod=='lastyear': + today = date.today() + startdate = today-timezone.timedelta(days=365) + enddate = today+timezone.timedelta(days=1) elif daterangetester.match(timeperiod): tstartdatestring = daterangetester.match(timeperiod).group(1) tenddatestring = daterangetester.match(timeperiod).group(2) diff --git a/rowers/tasks.py b/rowers/tasks.py index cbf98d8d..efc0cf44 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -68,6 +68,7 @@ from rowers.dataprepnodjango import ( # create_strava_stroke_data_db ) +from rowers.opaque import encoder from django.core.mail import ( send_mail, @@ -1890,7 +1891,8 @@ def handle_sendemailnewresponse(first_name, last_name, if 'sessiontype' in kwargs: sessiontype=kwargs.pop('sessiontype') - commentlink = '/rowers/workout/{workoutid}/comment/'.format(workoutid=workoutid) + commentlink = '/rowers/workout/{workoutid}/comment/'.format( + workoutid=encoder.encode_hex(workoutid)) if 'commentlink' in kwargs: commentlink = kwargs.pop('commentlink') @@ -1940,7 +1942,8 @@ def handle_sendemailnewcomment(first_name, if 'sessiontype' in kwargs: sessiontype=kwargs.pop('sessiontype') - commentlink = '/rowers/workout/{workoutid}/comment/'.format(workoutid=workoutid) + commentlink = '/rowers/workout/{workoutid}/comment/'.format( + workoutid=encoder.encode_hex(workoutid)) if 'commentlink' in kwargs: commentlink = kwargs.pop('commentlink') @@ -1951,7 +1954,7 @@ def handle_sendemailnewcomment(first_name, 'comment':comment, 'workoutname':workoutname, 'siteurl':siteurl, - 'workoutid':workoutid, + 'workoutid':encoder.encode_hex(workoutid), 'sessiontype':sessiontype, 'commentlink':commentlink, } diff --git a/rowers/teams.py b/rowers/teams.py index d934b446..8980e8a8 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -111,16 +111,20 @@ 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) coachgroup.save() coach.mycoachgroup = coachgroup coach.save() - - rower.coachinggroups.add(coach.mycoachgroup) - return (1,"Added Coach") + if get_coach_club_size(coach)=coach.clubsize: + return(0,'You have reached the maximum number of athletes') else: return (0,'You are not a coach') @@ -634,7 +649,7 @@ def process_coachrequest_code(coach,code): result = add_coach(coach,rekwest.user.rower) if not result: - return (result,"Something went wrong") + return result else: send_coachrequest_accepted_email(rekwest) @@ -655,7 +670,7 @@ def process_coachoffer_code(user,code): result = add_coach(rekwest.coach,rekwest.user.rower) if not result: - return (result,"Something went wrong") + return result else: send_coachoffer_accepted_email(rekwest) diff --git a/rowers/templates/downgrade.html b/rowers/templates/downgrade.html index 2ad91f7e..2956bcff 100644 --- a/rowers/templates/downgrade.html +++ b/rowers/templates/downgrade.html @@ -43,7 +43,7 @@

- Looking for the downgrade option? + Looking for the upgrade option?

  • diff --git a/rowers/templates/list_workouts.html b/rowers/templates/list_workouts.html index bfac39a8..b4066432 100644 --- a/rowers/templates/list_workouts.html +++ b/rowers/templates/list_workouts.html @@ -129,7 +129,7 @@ {% if workouts.has_next %} {% if request.GET.q %} - + {% else %} - + @@ -216,7 +216,7 @@ {% if team %} {% if workout|may_edit:request %} - + {{ workout.user.user.first_name }} {{ workout.user.user.last_name }} @@ -245,18 +245,14 @@ - {% if workout|may_edit:request %} - {% else %} -   - {% endif %} - {% if workout.user.user == user or user == team.manager %} + {% if workout|may_edit:request %} diff --git a/rowers/templates/menu_plan.html b/rowers/templates/menu_plan.html index d6a448ec..7177f790 100644 --- a/rowers/templates/menu_plan.html +++ b/rowers/templates/menu_plan.html @@ -63,7 +63,7 @@
  • -  Add Team Session +  Add Group Session
  • diff --git a/rowers/templates/menu_workout.html b/rowers/templates/menu_workout.html index 84ff5c97..b87876bb 100644 --- a/rowers/templates/menu_workout.html +++ b/rowers/templates/menu_workout.html @@ -41,7 +41,7 @@
  • - +  Compare
  • diff --git a/rowers/templates/paidplans.html b/rowers/templates/paidplans.html index b091b9e5..fd5e4655 100644 --- a/rowers/templates/paidplans.html +++ b/rowers/templates/paidplans.html @@ -94,7 +94,14 @@ ✔ - Create Training plans, tests and challenges for yourself. Track your performance + Create and manage groups. +   + ✔ + ✔ + ✔ + + + Create Training plans, tests and challenges for yourself and your training group. Track your performance against plan.     @@ -110,14 +117,21 @@ ✔ - Create and manage teams. + Manage your athlete's workouts       ✔ - Manage your athlete's workouts + Run analytics for your athletes +   +   +   + ✔ + + + Change zone intensities and other workout related settings for your athletes       diff --git a/rowers/templates/plannedsessionsmanage.html b/rowers/templates/plannedsessionsmanage.html index 0e12facf..fa78f7b1 100644 --- a/rowers/templates/plannedsessionsmanage.html +++ b/rowers/templates/plannedsessionsmanage.html @@ -90,7 +90,7 @@
  • {% csrf_token %} - +
  • diff --git a/rowers/templates/plannedsessionview.html b/rowers/templates/plannedsessionview.html index ee840e64..f5aa1c88 100644 --- a/rowers/templates/plannedsessionview.html +++ b/rowers/templates/plannedsessionview.html @@ -96,6 +96,10 @@ {% endfor %} +

    + + Add, remove or change workouts for this session +

  • {{ rower.user.first_name }} {{ rower.user.last_name }}

    diff --git a/rowers/templates/rower_exportsettings.html b/rowers/templates/rower_exportsettings.html index 8e4fd953..5138566c 100644 --- a/rowers/templates/rower_exportsettings.html +++ b/rowers/templates/rower_exportsettings.html @@ -4,6 +4,11 @@ {% block main %}

    Import and Export Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}

    + +{% if user.rower.rowerplan == 'basic' %} +The auto import and export settings only work on a paid plan. +{% endif %} + {% if form.errors %}

    Please correct the error{{ form.errors|pluralize }} below. diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 83269444..e44015eb 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -470,7 +470,7 @@ def userurl(path,member): userstring = 'user/%s/' % member.id # remove team - tpattern = re.compile('\/team\/\d+/') + tpattern = re.compile('team\/\d+/') if tpattern.search(path) is not None: path = tpattern.sub('',path) @@ -478,7 +478,7 @@ def userurl(path,member): replaced = pattern.sub(userstring,path) else: replaced = path+userstring - + return replaced @register.filter @@ -489,7 +489,7 @@ def teamurl(path,team): # remove user upattern = re.compile('\/user\/\d+/') if upattern.search(path) is not None: - path = upattern.sub('',path) + path = upattern.sub('/',path) if pattern.search(path) is not None: @@ -497,6 +497,7 @@ def teamurl(path,team): else: replaced = path+teamstring + return replaced @register.filter diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index 4b1122fd..a84f7982 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -20,7 +20,7 @@ class PermissionsBasicsTests(TestCase): self.rcoach = Rower.objects.create(user=self.ucoach, birthdate=faker.profile()['birthdate'], gdproptin=True,gdproptindate=timezone.now(), - rowerplan='coach') + rowerplan='coach',clubsize=10) self.ucoach_workouts = WorkoutFactory.create_batch(5, user=self.rcoach) self.coachinggroup = CoachingGroup.objects.create() @@ -280,7 +280,7 @@ class PermissionsViewTests(TestCase): self.rcoach = Rower.objects.create(user=self.ucoach, birthdate=faker.profile()['birthdate'], gdproptin=True,gdproptindate=timezone.now(), - rowerplan='coach') + rowerplan='coach',clubsize=10) self.ucoach_workouts = WorkoutFactory.create_batch(5, user=self.rcoach) self.coachinggroup = CoachingGroup.objects.create() @@ -958,6 +958,31 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) self.assertEqual(response.status_code,200) + + # stats + url = reverse('workout_view', + kwargs={'id':encoder.encode_hex(self.uplan2_workouts[0].id)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + # workflow + url = reverse('workout_workflow_view', + kwargs={'id':encoder.encode_hex(self.uplan2_workouts[0].id)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + # stats + url = reverse('workout_stats_view', + kwargs={'id':encoder.encode_hex(self.uplan2_workouts[0].id)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + ## Pro users (and higher) can join group led by other Pro (or higher) user def test_team_member_request_pro_pro(self): @@ -1009,7 +1034,7 @@ class PermissionsCoachingTests(TestCase): self.rcoach = Rower.objects.create(user=self.ucoach, birthdate=faker.profile()['birthdate'], gdproptin=True,gdproptindate=timezone.now(), - rowerplan='coach') + rowerplan='coach',clubsize=10) self.ucoach_workouts = WorkoutFactory.create_batch(5, user=self.rcoach) self.coachinggroup = CoachingGroup.objects.create() @@ -1416,8 +1441,7 @@ class PermissionsCoachingTests(TestCase): ## Basic users can subscribe to any race -# group related -## group members can see but not edit each other's workouts and charts +### ## group members can see but not edit each other's plans diff --git a/rowers/tests/testdata/testdata.csv.gz b/rowers/tests/testdata/testdata.csv.gz index ea8b8ca5..b6e2d423 100644 Binary files a/rowers/tests/testdata/testdata.csv.gz and b/rowers/tests/testdata/testdata.csv.gz differ diff --git a/rowers/views/statements.py b/rowers/views/statements.py index e7e2305e..3f1ec013 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -367,6 +367,14 @@ def get_workout_permitted(user,id): 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 total = 1 @@ -944,7 +952,9 @@ from rowers.utils import ( import rowers.datautils as datautils -from rowers.models import checkworkoutuser,checkaccessuser,checkviewworkouts +from rowers.models import ( + checkworkoutuser,checkaccessuser,checkviewworkouts,checkworkoutuserview + ) # Check if a user is a Coach member def iscoachmember(user): diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 224a2ef7..0fff0eb4 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -189,7 +189,9 @@ def rower_teams_view(request,message='',successmessage=''): coachrequests = CoachRequest.objects.filter(coach=r) invitedathletes = [rekwest.user for rekwest in mycoachoffers] - invitedcoaches = [rekwest.coach for rekwest in mycoachrequests] + invitedcoaches = [rekwest.coach for rekwest in mycoachrequests ] + invitedcoaches += [rekwest.coach for rekwest in coachoffers] + invitingathletes = [rekwest.user for rekwest in coachrequests] coaches = teams.rower_get_coaches(r) @@ -200,6 +202,7 @@ def rower_teams_view(request,message='',successmessage=''): ] potentialcoaches = list(set(potentialcoaches+offercoaches)) 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)