diff --git a/rowers/tests/test_plans.py b/rowers/tests/test_plans.py
index d6d73410..ff464120 100644
--- a/rowers/tests/test_plans.py
+++ b/rowers/tests/test_plans.py
@@ -486,6 +486,25 @@ class SessionCompleteTest(TestCase):
self.assertEqual(verdict,'on target')
+ def test_session_comment(self):
+ login = self.c.login(username=self.u.username, password=self.password)
+ self.assertTrue(login)
+
+
+ url = reverse('plannedsession_comment_view',kwargs={'id':self.ps_rscore.id})
+
+ response = self.c.get(url)
+ self.assertEqual(response.status_code,200)
+
+ form_data = {
+ 'comment': faker.text()
+ }
+
+ form = WorkoutCommentForm(form_data)
+ self.assertTrue(form.is_valid())
+
+ response = self.c.post(url,form_data,follow=True)
+ self.assertEqual(response.status_code,200)
def test_session1_exact_complete(self):
diff --git a/rowers/tests/testdata/testdata.csv.gz b/rowers/tests/testdata/testdata.csv.gz
index 813fcfa3..36e5640a 100644
Binary files a/rowers/tests/testdata/testdata.csv.gz and b/rowers/tests/testdata/testdata.csv.gz differ
diff --git a/rowers/urls.py b/rowers/urls.py
index 3fe4c64b..5200a70e 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -608,6 +608,10 @@ urlpatterns = [
name='plannedsessions_coach_view'),
url(r'^sessions/print/?/$',views.plannedsessions_print_view,
name='plannedsessions_print_view'),
+ url(r'^sessions/(?P\d+)/comments/user/(?P\d+)/$',views.plannedsession_comment_view,
+ name='plannedsession_comment_view'),
+ url(r'^sessions/(?P\d+)/comments/$',views.plannedsession_comment_view,
+ name='plannedsession_comment_view'),
url(r'^sessions/print/user/(?P\d+)/$',views.plannedsessions_print_view,
name='plannedsessions_print_view'),
url(r'^sessions/sendcalendar/$',views.plannedsessions_icsemail_view,
diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py
index 32de7ce1..f4d43afa 100644
--- a/rowers/views/planviews.py
+++ b/rowers/views/planviews.py
@@ -1,6 +1,150 @@
from statements import *
+@login_required()
+def plannedsession_comment_view(request,id=0,userid=0):
+ r = getrequestrower(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")
+
+ comments = PlannedSessionComment.objects.filter(plannedsession=ps).order_by("created")
+
+ if request.method == 'POST':
+ manager = ps.manager
+ form = PlannedSessionCommentForm(request.POST)
+ if form.is_valid():
+ cd = form.cleaned_data
+ comment = cd['comment']
+ comment = bleach.clean(comment)
+ if isinstance(comment,unicode):
+ comment = comment.encode('utf8')
+ elif isinstance(comment, str):
+ comment = comment.decode('utf8')
+
+ notification = cd['notification']
+ c = PlannedSessionComment(plannedsession=ps,user=request.user,comment=comment,
+ notification=notification)
+ c.save()
+ url = reverse('plannedsession_comment_view',
+ kwargs={
+ 'id':id
+ })
+ message = '{name} says: {comment}'.format(
+ name = request.user.first_name,
+ comment = comment,
+ url = url,
+ )
+ if request.user != manager:
+ a_messages.info(r.user,message.encode('ascii','ignore'))
+
+ sessiontype = 'training session'
+ if ps.sessiontype == 'race':
+ sessiontype = 'online virtual race'
+ elif ps.sessiontype == 'indoorrace':
+ sessiontype = 'indoor online virtual race'
+
+ res = myqueue(queuehigh,
+ handle_sendemailnewcomment,r.user.first_name,
+ r.user.last_name,
+ r.user.email,
+ request.user.first_name,
+ request.user.last_name,
+ comment,ps.name,ps.id,
+ emailbounced = r.emailbounced,
+ sessiontype = sessiontype,
+ commentlink = url
+ )
+
+ commenters = {oc.user for oc in comments if oc.notification}
+ for u in commenters:
+ a_messages.info(u,message)
+ if u != request.user and u != r.user:
+ ocr = Rower.objects.get(user=u)
+ res = myqueue(queuelow,
+ handle_sendemailnewresponse,
+ u.first_name,
+ u.last_name,
+ u.email,
+ request.user.first_name,
+ request.user.last_name,
+ comment,
+ ps.name,
+ ps.id,
+ c.id,
+ emailbounced = ocr.emailbounced,
+ sessiontype = sessiontype,
+ commentlink = url
+ )
+
+ url = reverse('plannedsession_comment_view',kwargs={'id':ps.id})
+ return HttpResponseRedirect(url)
+
+
+ form = WorkoutCommentForm()
+
+ rower = getrower(request.user)
+
+ if ps.sessiontype in ['race','indoorrace']:
+ breadcrumbs = [
+ {
+ 'url':reverse('virtualevents_view'),
+ 'name': 'Races'
+ },
+ {
+ 'url': reverse('virtualevent_view',kwargs={'id':ps.id}),
+ 'name': ps.name
+ },
+ {
+ 'url':reverse('plannedsession_comment_view',kwargs={'id':ps.id}),
+ 'name': 'Comments'
+ }
+ ]
+
+ active = 'nav-racing'
+
+ else:
+ breadcrumbs = [
+ {
+ 'url':reverse('virtualevents_view'),
+ 'name': 'Races'
+ },
+ {
+ 'url': reverse('virtualevent_view',kwargs={'id':ps.id}),
+ 'name': ps.name
+ },
+ {
+ 'url':reverse('plannedsession_comment_view',kwargs={'id':ps.id}),
+ 'name': 'Comments'
+ }
+ ]
+
+ active = 'nav-plan'
+
+ return render(request,
+ 'plannedsession_comments.html',
+ {'plannedsession':ps,
+ 'rower':rower,
+ 'breadcrumbs':breadcrumbs,
+ 'active':active,
+ 'comments':comments,
+ 'form':form,
+ })
+
# Cloning sessions
@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
@@ -1385,7 +1529,7 @@ def plannedsession_view(request,id=0,userid=0):
raise Http404("Planned Session does not exist")
if ps.sessiontype in ['race','indoorrace']:
- url = reverse(virtualevent_view,
+ url = reverse('virtualevent_view',
kwargs={'id':ps.id}
)
return HttpResponseRedirect(url)
@@ -1522,6 +1666,8 @@ def plannedsession_view(request,id=0,userid=0):
}
]
+ comments = PlannedSessionComment.objects.filter(plannedsession=ps).order_by("created")
+
return render(request,'plannedsessionview.html',
{
'psdict': psdict,
@@ -1544,7 +1690,8 @@ def plannedsession_view(request,id=0,userid=0):
'timeperiod':timeperiod,
'ranking':ranking,
'coursescript': coursescript,
- 'coursediv': coursediv
+ 'coursediv': coursediv,
+ 'comments': comments,
}
)
diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py
index 139d82c3..e7addf43 100644
--- a/rowers/views/racesviews.py
+++ b/rowers/views/racesviews.py
@@ -882,6 +882,9 @@ def virtualevent_view(request,id=0):
else:
racelogo = None
+ comments = PlannedSessionComment.objects.filter(plannedsession=race).order_by("created")
+
+
return render(request,'virtualevent.html',
{
'coursescript':script,
@@ -896,6 +899,7 @@ def virtualevent_view(request,id=0):
'racelogo':racelogo,
'form':form,
'active':'nav-racing',
+ 'comments':comments
})
def virtualevent_ranking_view(request,id=0):
diff --git a/rowers/views/statements.py b/rowers/views/statements.py
index 2af32b38..ee597981 100644
--- a/rowers/views/statements.py
+++ b/rowers/views/statements.py
@@ -87,6 +87,7 @@ from rowers.models import (
microcyclecheckdates,mesocyclecheckdates,macrocyclecheckdates,
TrainingMesoCycleForm, TrainingMicroCycleForm,
RaceLogo,RowerBillingAddressForm,PaidPlan,
+ PlannedSessionComment,
)
from rowers.models import (
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
@@ -97,7 +98,7 @@ from rowers.models import (
PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace,
VirtualRaceForm,VirtualRaceResultForm,RowerImportExportForm,
IndoorVirtualRaceResultForm,IndoorVirtualRaceResult,
- IndoorVirtualRaceForm,
+ IndoorVirtualRaceForm,PlannedSessionCommentForm,
)
from rowers.models import (
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet,
Comments ({{ comments|length }})
+ {% for c in comments %} ++ {{ c.user.first_name }} {{ c.user.last_name }} - {{ c.created }} + +
+ {% endfor %} ++ Add a comment +
+