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 }}
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; +}