created WorkoutComment model
This commit is contained in:
@@ -855,6 +855,22 @@ class RowerForm(ModelForm):
|
|||||||
if an>=max:
|
if an>=max:
|
||||||
raise forms.ValidationError("AN should be lower than 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
|
# An announcement that goes to the right of the workouts list
|
||||||
# optionally sends a tweet to our twitter account
|
# optionally sends a tweet to our twitter account
|
||||||
class SiteAnnouncement(models.Model):
|
class SiteAnnouncement(models.Model):
|
||||||
|
|||||||
192
rowers/templates/workout_comments.html
Normal file
192
rowers/templates/workout_comments.html
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
{% load tz %}
|
||||||
|
|
||||||
|
{% block title %}Change Workout {% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div id="workouts" class="grid_6 alpha">
|
||||||
|
|
||||||
|
{% if form.errors %}
|
||||||
|
<p style="color: red;">
|
||||||
|
Please correct the error{{ form.errors|pluralize }} below.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<h1>Comments {{ workout.name }}</h1>
|
||||||
|
|
||||||
|
{% localtime on %}
|
||||||
|
<table width=100%>
|
||||||
|
<tr>
|
||||||
|
<th>Rower:</th><td>{{ first_name }} {{ last_name }}</td>
|
||||||
|
</tr><tr>
|
||||||
|
<th>Date/Time:</th><td>{{ workout.startdatetime }}</td>
|
||||||
|
</tr><tr>
|
||||||
|
<th>Distance:</th><td>{{ workout.distance }}m</td>
|
||||||
|
</tr><tr>
|
||||||
|
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
|
||||||
|
</tr><tr>
|
||||||
|
<th>Public link to this workout</th>
|
||||||
|
<td>
|
||||||
|
<a href="/rowers/workout/{{ workout.id }}">https://rowsandall.com/rowers/workout/{{ workout.id }}</a>
|
||||||
|
<td>
|
||||||
|
</tr><tr>
|
||||||
|
<th>Public link to interactive chart</th>
|
||||||
|
<td>
|
||||||
|
<a href="/rowers/workout/{{ workout.id }}/interactiveplot">https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot</a>
|
||||||
|
<td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
{% endlocaltime %}
|
||||||
|
<form enctype="multipart/form-data" action="" method="post">
|
||||||
|
<table width=100%>
|
||||||
|
{{ form.as_table }}
|
||||||
|
</table>
|
||||||
|
{% csrf_token %}
|
||||||
|
<div id="formbutton" class="grid_1 prefix_4 suffix_1 omega">
|
||||||
|
<input class="button green" type="submit" value="Save">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id="images" class="grid_6 omega">
|
||||||
|
<h1>Images linked to this workout</h1>
|
||||||
|
<div class="grid_6 alpha">
|
||||||
|
<div class="grid_2 alpha">
|
||||||
|
<p>
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addtimeplot">Add Time Plot</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="grid_2">
|
||||||
|
<p>
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/adddistanceplot">Add Distance Plot</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="grid_2 omega">
|
||||||
|
<p>
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addpiechart">Add Pie Chart</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="grid_6 alpha">
|
||||||
|
|
||||||
|
<div class="grid_2 prefix_4 alpha">
|
||||||
|
<p>
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addpowerpiechart">Power Pie Chart</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="grid_6">
|
||||||
|
<p>Generating images takes roughly 1 second per minute
|
||||||
|
of your workout's duration. Please reload after a minute or so.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if graphs1 %}
|
||||||
|
|
||||||
|
{% for graph in graphs1 %}
|
||||||
|
{% if forloop.counter == 1 %}
|
||||||
|
<div id="thumb-container" class="grid_2 alpha">
|
||||||
|
<a href="/rowers/graph/{{ graph.id }}/">
|
||||||
|
<img src="/{{ graph.filename }}"
|
||||||
|
onerror="this.src='/static/img/waiting.png'"
|
||||||
|
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||||
|
</div>
|
||||||
|
{% elif forloop.counter == 3 %}
|
||||||
|
<div id="thumb-container" class="grid_2 omega">
|
||||||
|
<a href="/rowers/graph/{{ graph.id }}/">
|
||||||
|
<img src="/{{ graph.filename }}"
|
||||||
|
onerror="this.src='/static/img/waiting.png'"
|
||||||
|
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<div id="thumb-container" class="grid_2">
|
||||||
|
<a href="/rowers/graph/{{ graph.id }}/">
|
||||||
|
<img src="/{{ graph.filename }}"
|
||||||
|
onerror="this.src='/static/img/waiting.png'"
|
||||||
|
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
|
||||||
|
{% for graph in graphs2 %}
|
||||||
|
{% if forloop.counter == 1 %}
|
||||||
|
<div id="thumb-container" class="grid_2 alpha">
|
||||||
|
<a href="/rowers/graph/{{ graph.id }}/">
|
||||||
|
<img src="/{{ graph.filename }}"
|
||||||
|
onerror="this.src='/static/img/waiting.png'"
|
||||||
|
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||||
|
</div>
|
||||||
|
{% elif forloop.counter == 3 %}
|
||||||
|
<div id="thumb-container" class="grid_2 omega">
|
||||||
|
<a href="/rowers/graph/{{ graph.id }}/">
|
||||||
|
<img src="/{{ graph.filename }}"
|
||||||
|
onerror="this.src='/static/img/waiting.png'"
|
||||||
|
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<div id="thumb-container" class="grid_2">
|
||||||
|
<a href="/rowers/graph/{{ graph.id }}/">
|
||||||
|
<img src="/{{ graph.filename }}"
|
||||||
|
onerror="this.src='/static/img/waiting.png'"
|
||||||
|
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<p> No graphs found </p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
|
||||||
|
html, body {height: 100%; margin:5px;}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="summary" class="grid_6 omega">
|
||||||
|
<h1>Workout Summary</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<pre>
|
||||||
|
{{ workout.summary }}
|
||||||
|
</pre>
|
||||||
|
</p>
|
||||||
|
<div class="grid_2 alpha">
|
||||||
|
<a class="button green small" href="recalcsummary">Update Summary</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="interactiveplot" class="grid_6 omega">
|
||||||
|
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
|
||||||
|
<script async="true" type="text/javascript">
|
||||||
|
Bokeh.set_log_level("info");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{ gmscript |safe }}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
|
||||||
|
html, body, #map_canvas {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin:0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#map_canvas_container {position: relative; top:0; right:0; bottom:0; left:0;}
|
||||||
|
|
||||||
|
#map_canvas {position: relative; top: 0; right: 0; bottom: 0; left: 0;}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<div id="map_canvas">
|
||||||
|
{{ gmdiv|safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h1>Edit Workout Data</h1>
|
<h1>Edit Workout {{ workout.name }}</h1>
|
||||||
<div class="grid_6 alpha">
|
<div class="grid_6 alpha">
|
||||||
<div class="grid_2 alpha">
|
<div class="grid_2 alpha">
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<div id="workouts" class="grid_6 alpha">
|
<div id="workouts" class="grid_6 alpha">
|
||||||
|
|
||||||
|
|
||||||
<h1>Workout Data</h1>
|
<h1>{{ workout.name }}</h1>
|
||||||
<table width=100%>
|
<table width=100%>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Rower:</th><td>{{ first_name }} {{ last_name }}</td>
|
<th>Rower:</th><td>{{ first_name }} {{ last_name }}</td>
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ urlpatterns = [
|
|||||||
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)$',views.workout_export_view),
|
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)$',views.workout_export_view),
|
||||||
url(r'^workout/(?P<id>\d+)/export/s/(?P<successmessage>\w+.*)$',views.workout_export_view),
|
url(r'^workout/(?P<id>\d+)/export/s/(?P<successmessage>\w+.*)$',views.workout_export_view),
|
||||||
url(r'^workout/(?P<id>\d+)/export$',views.workout_export_view),
|
url(r'^workout/(?P<id>\d+)/export$',views.workout_export_view),
|
||||||
|
url(r'^workout/(?P<id>\d+)/comment$',views.workout_comment_view),
|
||||||
url(r'^workout/(\d+)/emailtcx$',views.workout_tcxemail_view),
|
url(r'^workout/(\d+)/emailtcx$',views.workout_tcxemail_view),
|
||||||
url(r'^workout/(\d+)/emailcsv$',views.workout_csvemail_view),
|
url(r'^workout/(\d+)/emailcsv$',views.workout_csvemail_view),
|
||||||
url(r'^workout/compare/(\d+)/$',views.workout_comparison_list),
|
url(r'^workout/compare/(\d+)/$',views.workout_comparison_list),
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ from rowers.models import Workout, User, Rower, WorkoutForm,FavoriteChart
|
|||||||
from rowers.models import (
|
from rowers.models import (
|
||||||
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
|
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
|
||||||
RowerPowerZonesForm,AccountRowerForm,UserForm,StrokeData,
|
RowerPowerZonesForm,AccountRowerForm,UserForm,StrokeData,
|
||||||
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest
|
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
|
||||||
|
WorkoutComment,WorkoutCommentForm
|
||||||
)
|
)
|
||||||
from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement
|
from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement
|
||||||
from django.forms.formsets import formset_factory
|
from django.forms.formsets import formset_factory
|
||||||
@@ -3314,6 +3315,26 @@ def workout_export_view(request,id=0, message="", successmessage=""):
|
|||||||
'message':message,
|
'message':message,
|
||||||
'successmessage':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
|
# The basic edit page
|
||||||
@login_required()
|
@login_required()
|
||||||
|
|||||||
Reference in New Issue
Block a user