From 7ffaf1837970840641b03848565ec18edd69dc97 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 17 Feb 2017 10:27:45 +0100 Subject: [PATCH 1/3] created WorkoutComment model --- rowers/models.py | 16 +++ rowers/templates/workout_comments.html | 192 +++++++++++++++++++++++++ rowers/templates/workout_form.html | 2 +- rowers/templates/workout_view.html | 2 +- rowers/urls.py | 1 + rowers/views.py | 23 ++- 6 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 rowers/templates/workout_comments.html diff --git a/rowers/models.py b/rowers/models.py index cc8883d9..af9aa4ff 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -855,6 +855,22 @@ class RowerForm(ModelForm): if an>=max: raise forms.ValidationError("AN should be lower than Max") +# A comment by a user on a training +class WorkoutComment(models.Model): + created = models.DateField(default=timezone.now) + comment = models.TextField(max_length=300) + read = models.BooleanField(default=False) + user = models.ForeignKey(User) + workout = models.ForeignKey(Workout) + +class WorkoutCommentForm(ModelForm): + class Meta: + model = WorkoutComment + fields = ['comment'] + widgets = { + 'comment': forms.Textarea, + } + # An announcement that goes to the right of the workouts list # optionally sends a tweet to our twitter account class SiteAnnouncement(models.Model): diff --git a/rowers/templates/workout_comments.html b/rowers/templates/workout_comments.html new file mode 100644 index 00000000..d87b5cf2 --- /dev/null +++ b/rowers/templates/workout_comments.html @@ -0,0 +1,192 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load rowerfilters %} +{% load tz %} + +{% block title %}Change Workout {% endblock %} + +{% block content %} +
+ + {% if form.errors %} +

+ Please correct the error{{ form.errors|pluralize }} below. +

+ {% endif %} + +

Comments {{ workout.name }}

+ +{% localtime on %} + + + + + + + + + + + + + + +
Rower:{{ first_name }} {{ last_name }}
Date/Time:{{ workout.startdatetime }}
Distance:{{ workout.distance }}m
Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
Public link to this workout + https://rowsandall.com/rowers/workout/{{ workout.id }} + +
Public link to interactive chart + https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot + +
+{% endlocaltime %} +
+ + {{ form.as_table }} +
+ {% csrf_token %} +
+ +
+
+
+
+

Images linked to this workout

+
+
+

+ Add Time Plot +

+
+ +
+

+ Add Pie Chart +

+
+
+ +
+

+ Power Pie Chart +

+
+
+

Generating images takes roughly 1 second per minute + of your workout's duration. Please reload after a minute or so.

+
+
+ {% if graphs1 %} + + {% for graph in graphs1 %} + {% if forloop.counter == 1 %} +
+ + {{ graph.filename }} +
+ {% elif forloop.counter == 3 %} +
+ + {{ graph.filename }} +
+ + {% else %} +
+ + {{ graph.filename }} +
+ {% endif %} + {% endfor %} + + + {% for graph in graphs2 %} + {% if forloop.counter == 1 %} +
+ + {{ graph.filename }} +
+ {% elif forloop.counter == 3 %} +
+ + {{ graph.filename }} +
+ + {% else %} +
+ + {{ graph.filename }} +
+ {% endif %} + {% endfor %} + + + + + {% else %} +

No graphs found

+ {% endif %} +
+ + + +
+

Workout Summary

+ +

+

+{{ workout.summary }}
+
+

+ + +
+ + + +
+ + + + {{ gmscript |safe }} + + +
+ {{ gmdiv|safe }} +
+
+ +{% endblock %} diff --git a/rowers/templates/workout_form.html b/rowers/templates/workout_form.html index 356a245a..e47136e8 100644 --- a/rowers/templates/workout_form.html +++ b/rowers/templates/workout_form.html @@ -14,7 +14,7 @@

{% endif %} -

Edit Workout Data

+

Edit Workout {{ workout.name }}

diff --git a/rowers/templates/workout_view.html b/rowers/templates/workout_view.html index 2bee8359..3bbb4d4b 100644 --- a/rowers/templates/workout_view.html +++ b/rowers/templates/workout_view.html @@ -8,7 +8,7 @@

-

Workout Data

+

{{ workout.name }}

diff --git a/rowers/urls.py b/rowers/urls.py index 37ec074b..e54bbac2 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -148,6 +148,7 @@ urlpatterns = [ url(r'^workout/(?P\d+)/export/c/(?P\w+.*)$',views.workout_export_view), url(r'^workout/(?P\d+)/export/s/(?P\w+.*)$',views.workout_export_view), url(r'^workout/(?P\d+)/export$',views.workout_export_view), + url(r'^workout/(?P\d+)/comment$',views.workout_comment_view), url(r'^workout/(\d+)/emailtcx$',views.workout_tcxemail_view), url(r'^workout/(\d+)/emailcsv$',views.workout_csvemail_view), url(r'^workout/compare/(\d+)/$',views.workout_comparison_list), diff --git a/rowers/views.py b/rowers/views.py index 9250f6c0..43cfea8b 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -32,7 +32,8 @@ from rowers.models import Workout, User, Rower, WorkoutForm,FavoriteChart from rowers.models import ( RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm, RowerPowerZonesForm,AccountRowerForm,UserForm,StrokeData, - Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest + Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest, + WorkoutComment,WorkoutCommentForm ) from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement from django.forms.formsets import formset_factory @@ -3314,6 +3315,26 @@ def workout_export_view(request,id=0, message="", successmessage=""): 'successmessage':successmessage, }) +# list of comments to a workout +@login_required() +def workout_comment_view(request,id=0): + try: + w = Workout.objects.get(id=id) + except Workout.DoesNotExist: + raise Http404("Workout doesn't exist") + + if w.privacy == 'private' and w.user.user != request.user: + return HttpResponseForbidden("Permission error") + + # ok we're permitted + comments = WorkoutComment.objects.filter(workout=w) + + return render(request, + 'workout_comments.html', + {'workout':w, + 'comments':comments, + }) + # The basic edit page @login_required() def workout_edit_view(request,id=0,message="",successmessage=""): From 53f51c456fd95c62992a4b5c251d6cc879d11843 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 17 Feb 2017 16:13:34 +0100 Subject: [PATCH 2/3] implemented comments page --- rowers/admin.py | 7 +- rowers/models.py | 40 ++-- rowers/templates/list_workouts.html | 8 +- rowers/templates/workout_comments.html | 307 ++++++++++++------------- rowers/views.py | 13 +- static/css/rowsandall.css | 254 ++++++++++++++++++++ 6 files changed, 455 insertions(+), 174 deletions(-) diff --git a/rowers/admin.py b/rowers/admin.py index e814d391..ea7a79ce 100644 --- a/rowers/admin.py +++ b/rowers/admin.py @@ -4,7 +4,8 @@ from django.contrib.auth.models import User from .models import ( Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement, - Team,TeamInvite,TeamRequest + Team,TeamInvite,TeamRequest, + WorkoutComment, ) # Register your models here so you can use them in the Admin module @@ -36,6 +37,9 @@ class TeamInviteAdmin(admin.ModelAdmin): class TeamRequestAdmin(admin.ModelAdmin): list_display = ('issuedate','team','user','code') + +class WorkoutCommentAdmin(admin.ModelAdmin): + list_display = ('created','user','workout') admin.site.unregister(User) admin.site.register(User,UserAdmin) @@ -46,3 +50,4 @@ admin.site.register(FavoriteChart,FavoriteChartAdmin) admin.site.register(SiteAnnouncement,SiteAnnouncementAdmin) admin.site.register(TeamInvite,TeamInviteAdmin) admin.site.register(TeamRequest,TeamRequestAdmin) +admin.site.register(WorkoutComment,WorkoutCommentAdmin) diff --git a/rowers/models.py b/rowers/models.py index af9aa4ff..c1dd3bbb 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -855,21 +855,6 @@ class RowerForm(ModelForm): if an>=max: raise forms.ValidationError("AN should be lower than Max") -# A comment by a user on a training -class WorkoutComment(models.Model): - created = models.DateField(default=timezone.now) - comment = models.TextField(max_length=300) - read = models.BooleanField(default=False) - user = models.ForeignKey(User) - workout = models.ForeignKey(Workout) - -class WorkoutCommentForm(ModelForm): - class Meta: - model = WorkoutComment - fields = ['comment'] - widgets = { - 'comment': forms.Textarea, - } # An announcement that goes to the right of the workouts list # optionally sends a tweet to our twitter account @@ -894,3 +879,28 @@ class SiteAnnouncement(models.Model): except: pass return super(SiteAnnouncement,self).save(*args, **kwargs) + +# A comment by a user on a training +class WorkoutComment(models.Model): + comment = models.TextField(max_length=300) + created = models.DateTimeField(default=timezone.now) + read = models.BooleanField(default=False) + user = models.ForeignKey(User) + workout = models.ForeignKey(Workout) + + def __unicode__(self): + return u'Comment to: {w} by {u1} {u2}'.format( + w=self.workout.name, + u1 = self.user.first_name, + u2 = self.user.last_name, + ) + + +class WorkoutCommentForm(ModelForm): + class Meta: + model = WorkoutComment + fields = ['comment',] + widgets = { + 'comment': forms.Textarea, + } + diff --git a/rowers/templates/list_workouts.html b/rowers/templates/list_workouts.html index 7a1d58ea..891e596b 100644 --- a/rowers/templates/list_workouts.html +++ b/rowers/templates/list_workouts.html @@ -5,6 +5,12 @@ {% block title %}Workouts{% endblock %} {% block content %} + + + +
Select start and end date for a date range: @@ -27,7 +33,7 @@
-
+
{% if team %}

{{ team.name }} Team Workouts

{% else %} diff --git a/rowers/templates/workout_comments.html b/rowers/templates/workout_comments.html index d87b5cf2..fec9ab39 100644 --- a/rowers/templates/workout_comments.html +++ b/rowers/templates/workout_comments.html @@ -6,172 +6,167 @@ {% block title %}Change Workout {% endblock %} {% block content %} -
+ - {% if form.errors %} -

- Please correct the error{{ form.errors|pluralize }} below. -

- {% endif %} - -

Comments {{ workout.name }}

- -{% localtime on %} -
Rower:{{ first_name }} {{ last_name }}
- - - - - - - - - - - - - +
Rower:{{ first_name }} {{ last_name }}
Date/Time:{{ workout.startdatetime }}
Distance:{{ workout.distance }}m
Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} - -
Public link to interactive chart +
+
+ {% if form.errors %} +

+ Please correct the error{{ form.errors|pluralize }} below. +

+ {% endif %} + +

Comments {{ workout.name }}

+ + {% localtime on %} + + + + + + + + + + + + + + -
Rower:{{ first_name }} {{ last_name }}
Date/Time:{{ workout.startdatetime }}
Distance:{{ workout.distance }}m
Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
Public link to this workout + https://rowsandall.com/rowers/workout/{{ workout.id }} + +
Public link to interactive chart https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot - -
-{% endlocaltime %} -
- - {{ form.as_table }} -
- {% csrf_token %} -
- -
-
-
-
-

Images linked to this workout

-
-
-

- Add Time Plot -

-
- -
-

- Add Pie Chart -

-
-
- -
-

- Power Pie Chart -

-
-
-

Generating images takes roughly 1 second per minute - of your workout's duration. Please reload after a minute or so.

-
+
+
+ {% endlocaltime %} +
+ {% for c in comments %} +
+
+ {{ c.created }} + {{ c.user.first_name }} {{ c.user.last_name }} +
+
+
+
+ {{ c.comment }} +
- {% if graphs1 %} +
+
+ {% endfor %} - {% for graph in graphs1 %} - {% if forloop.counter == 1 %} -
- - {{ graph.filename }} -
- {% elif forloop.counter == 3 %} -
- - {{ graph.filename }} -
- - {% else %} -
- - {{ graph.filename }} -
- {% endif %} - {% endfor %} - - - {% for graph in graphs2 %} - {% if forloop.counter == 1 %} -
- - {{ graph.filename }} -
- {% elif forloop.counter == 3 %} -
- - {{ graph.filename }} -
- - {% else %} -
- - {{ graph.filename }} -
- {% endif %} - {% endfor %} - - - - - {% else %} -

No graphs found

- {% endif %} +
+
+ + {{ form.as_table }} +
+ {% csrf_token %} +
+ +
+
+
+
- +
+

Images linked to this workout

+ + {% if graphs1 %} + + {% for graph in graphs1 %} + {% if forloop.counter == 1 %} +
+ + {{ graph.filename }} +
+ {% elif forloop.counter == 3 %} +
+ + {{ graph.filename }} +
+ + {% else %} +
+ + {{ graph.filename }} +
+ {% endif %} + {% endfor %} + + + {% for graph in graphs2 %} + {% if forloop.counter == 1 %} +
+ + {{ graph.filename }} +
+ {% elif forloop.counter == 3 %} +
+ + {{ graph.filename }} +
+ + {% else %} +
+ + {{ graph.filename }} +
+ {% endif %} + {% endfor %} + + + + + {% else %} +

No graphs found

+ {% endif %} +
+ +
-

Workout Summary

- -

-

-{{ workout.summary }}
-
-

- - +

Workout Summary

+ +

+

+      {{ workout.summary }}
+    
+

+
- - + - - {{ gmscript |safe }} - - -
- {{ gmdiv|safe }} -
+ +
+ {{ gmdiv|safe }} +
{% endblock %} diff --git a/rowers/views.py b/rowers/views.py index 43cfea8b..ae25f132 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -3327,12 +3327,23 @@ def workout_comment_view(request,id=0): return HttpResponseForbidden("Permission error") # ok we're permitted - comments = WorkoutComment.objects.filter(workout=w) + if request.method == 'POST': + form = WorkoutCommentForm(request.POST) + if form.is_valid(): + cd = form.cleaned_data + comment = cd['comment'] + c = WorkoutComment(workout=w,user=request.user,comment=comment) + c.save() + + comments = WorkoutComment.objects.filter(workout=w).order_by("created") + form = WorkoutCommentForm() + return render(request, 'workout_comments.html', {'workout':w, 'comments':comments, + 'form':form, }) # The basic edit page diff --git a/static/css/rowsandall.css b/static/css/rowsandall.css index 253f5e5d..09e1f881 100644 --- a/static/css/rowsandall.css +++ b/static/css/rowsandall.css @@ -554,3 +554,257 @@ a.wh:hover { .bk-canvas-map { overflow: hidden; } + + +/* container */ +.container { + padding: 5% 5%; +} + +/* CSS talk bubble */ +.talk-bubble { + margin: 40px; + display: inline-block; + position: relative; + width: 200px; + height: auto; + background-color: lightyellow; +} +.border{ + border: 8px solid #666; +} +.round{ + border-radius: 30px; + -webkit-border-radius: 30px; + -moz-border-radius: 30px; + +} + +/* Right triangle placed top left flush. */ +.tri-right.border.left-top:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -40px; + right: auto; + top: -8px; + bottom: auto; + border: 32px solid; + border-color: #666 transparent transparent transparent; +} +.tri-right.left-top:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -20px; + right: auto; + top: 0px; + bottom: auto; + border: 22px solid; + border-color: lightyellow transparent transparent transparent; +} + +/* Right triangle, left side slightly down */ +.tri-right.border.left-in:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -40px; + right: auto; + top: 30px; + bottom: auto; + border: 20px solid; + border-color: #666 #666 transparent transparent; +} +.tri-right.left-in:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -20px; + right: auto; + top: 38px; + bottom: auto; + border: 12px solid; + border-color: lightyellow lightyellow transparent transparent; +} + +/*Right triangle, placed bottom left side slightly in*/ +.tri-right.border.btm-left:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -8px; + right: auto; + top: auto; + bottom: -40px; + border: 32px solid; + border-color: transparent transparent transparent #666; +} +.tri-right.btm-left:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: 0px; + right: auto; + top: auto; + bottom: -20px; + border: 22px solid; + border-color: transparent transparent transparent lightyellow; +} + +/*Right triangle, placed bottom left side slightly in*/ +.tri-right.border.btm-left-in:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: 30px; + right: auto; + top: auto; + bottom: -40px; + border: 20px solid; + border-color: #666 transparent transparent #666; +} +.tri-right.btm-left-in:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: 38px; + right: auto; + top: auto; + bottom: -20px; + border: 12px solid; + border-color: lightyellow transparent transparent lightyellow; +} + +/*Right triangle, placed bottom right side slightly in*/ +.tri-right.border.btm-right-in:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: 30px; + bottom: -40px; + border: 20px solid; + border-color: #666 #666 transparent transparent; +} +.tri-right.btm-right-in:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: 38px; + bottom: -20px; + border: 12px solid; + border-color: lightyellow lightyellow transparent transparent; +} +/* + left: -8px; + right: auto; + top: auto; + bottom: -40px; + border: 32px solid; + border-color: transparent transparent transparent #666; + left: 0px; + right: auto; + top: auto; + bottom: -20px; + border: 22px solid; + border-color: transparent transparent transparent lightyellow; + +/*Right triangle, placed bottom right side slightly in*/ +.tri-right.border.btm-right:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -8px; + bottom: -40px; + border: 20px solid; + border-color: #666 #666 transparent transparent; +} +.tri-right.btm-right:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: 0px; + bottom: -20px; + border: 12px solid; + border-color: lightyellow lightyellow transparent transparent; +} + +/* Right triangle, right side slightly down*/ +.tri-right.border.right-in:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -40px; + top: 30px; + bottom: auto; + border: 20px solid; + border-color: #666 transparent transparent #666; +} +.tri-right.right-in:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -20px; + top: 38px; + bottom: auto; + border: 12px solid; + border-color: lightyellow transparent transparent lightyellow; +} + +/* Right triangle placed top right flush. */ +.tri-right.border.right-top:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -40px; + top: -8px; + bottom: auto; + border: 32px solid; + border-color: #666 transparent transparent transparent; +} +.tri-right.right-top:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -20px; + top: 0px; + bottom: auto; + border: 20px solid; + border-color: lightyellow transparent transparent transparent; +} + +/* talk bubble contents */ +.talktext{ + padding: 1em; + text-align: left; + line-height: 1.5em; +} +.talktext p{ + /* remove webkit p margins */ + -webkit-margin-before: 0em; + -webkit-margin-after: 0em; +} From cee6179416cc9f855cbbbed35aee1ff49e30fbca Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 17 Feb 2017 16:35:34 +0100 Subject: [PATCH 3/3] added comment functionality --- rowers/tasks.py | 29 ++++++++++++++++++++++++++ rowers/templates/workout_comments.html | 4 ---- rowers/templates/workout_form.html | 10 +++++++-- rowers/templates/workout_view.html | 6 ++++++ rowers/views.py | 24 +++++++++++++++++++-- 5 files changed, 65 insertions(+), 8 deletions(-) diff --git a/rowers/tasks.py b/rowers/tasks.py index 055dd524..e0367862 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -273,6 +273,35 @@ def handle_sendemail_invite(email,name,code,teamname,manager): return 1 +@app.task +def handle_sendemailnewcomment(first_name, + last_name, + email, + commenter_first_name, + commenter_last_name, + comment,workoutname, + workoutid): + fullemail = first_name+' '+last_name+' <'+email+'>' + subject = 'New comment on workout '+workoutname + message = 'Dear '+first_name+',\n\n' + message += commenter_first_name+' '+commenter_last_name + message += ' has written a new comment on your workout ' + message += workoutname+'\n\n' + message += comment + message += '\n\n' + message += 'You can read the comment here:\n' + message += 'https://rowsandall.com/rowers/workout/'+str(workoutid)+'/comment' + + email = EmailMessage(subject, message, + 'Rowsandall ', + [fullemail]) + + + res = email.send() + + return 1 + + @app.task def handle_sendemail_request(email,name,code,teamname,requestor,id): fullemail = name+' <'+email+'>' diff --git a/rowers/templates/workout_comments.html b/rowers/templates/workout_comments.html index fec9ab39..438388a5 100644 --- a/rowers/templates/workout_comments.html +++ b/rowers/templates/workout_comments.html @@ -6,10 +6,6 @@ {% block title %}Change Workout {% endblock %} {% block content %} - -
{% if form.errors %} diff --git a/rowers/templates/workout_form.html b/rowers/templates/workout_form.html index e47136e8..9c611295 100644 --- a/rowers/templates/workout_form.html +++ b/rowers/templates/workout_form.html @@ -54,8 +54,14 @@ Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} - + https://rowsandall.com/rowers/workout/{{ workout.id }} + + + Comments + + Comment + + Public link to interactive chart diff --git a/rowers/templates/workout_view.html b/rowers/templates/workout_view.html index 3bbb4d4b..38f0687e 100644 --- a/rowers/templates/workout_view.html +++ b/rowers/templates/workout_view.html @@ -29,6 +29,12 @@ Weight Category:{{ workout.weightcategory }} + + Comments + + Comment + +

Workout Summary

diff --git a/rowers/views.py b/rowers/views.py index ae25f132..950e810f 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -59,7 +59,9 @@ from rest_framework.renderers import JSONRenderer from rest_framework.parsers import JSONParser from rowers.rows import handle_uploaded_file from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv -from rowers.tasks import handle_sendemail_unrecognized +from rowers.tasks import ( + handle_sendemail_unrecognized,handle_sendemailnewcomment + ) from scipy.signal import savgol_filter from django.shortcuts import render_to_response @@ -3328,13 +3330,31 @@ def workout_comment_view(request,id=0): # ok we're permitted if request.method == 'POST': + r = w.user form = WorkoutCommentForm(request.POST) if form.is_valid(): cd = form.cleaned_data comment = cd['comment'] c = WorkoutComment(workout=w,user=request.user,comment=comment) c.save() - + if settings.DEBUG: + res = handle_sendemailnewcomment.delay(r.user.first_name, + r.user.last_name, + r.user.email, + c.user.first_name, + c.user.last_name, + comment,w.name, + w.id) + + else: + res = queuehigh.enqueue(handle_sendemailnewcomment,r.user.first_name, + r.user.last_name, + r.user.email, + r.user.first_name, + r.user.last_name, + comment,w.name,w.id) + + comments = WorkoutComment.objects.filter(workout=w).order_by("created") form = WorkoutCommentForm()