Private
Public Access
1
0

Merge branch 'release/1.2'

This commit is contained in:
Sander Roosendaal
2017-02-23 15:48:07 +01:00
8 changed files with 177 additions and 61 deletions

View File

@@ -19,7 +19,7 @@ from bokeh.layouts import layout,widgetbox
from bokeh.layouts import row as layoutrow from bokeh.layouts import row as layoutrow
from bokeh.layouts import column as layoutcolumn from bokeh.layouts import column as layoutcolumn
from bokeh.models import LinearAxis,LogAxis,Range1d,DatetimeTickFormatter,HoverTool from bokeh.models import LinearAxis,LogAxis,Range1d,DatetimeTickFormatter,HoverTool
from bokeh.io import output_file, show from bokeh.io import output_file, show, vplot
from bokeh.models import ( from bokeh.models import (
GMapPlot, GMapOptions, ColumnDataSource, Circle, GMapPlot, GMapOptions, ColumnDataSource, Circle,
DataRange1d, PanTool, WheelZoomTool, BoxSelectTool, DataRange1d, PanTool, WheelZoomTool, BoxSelectTool,
@@ -1604,10 +1604,6 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line',
if datadf.empty: if datadf.empty:
return ['','<p>No non-zero data in selection</p>','',''] return ['','<p>No non-zero data in selection</p>','','']
if xparam != 'distance' and xparam != 'time' and xparam != 'cumdist': if xparam != 'distance' and xparam != 'time' and xparam != 'cumdist':
xaxmax = yaxmaxima[xparam] xaxmax = yaxmaxima[xparam]
xaxmin = yaxminima[xparam] xaxmin = yaxminima[xparam]
@@ -1673,17 +1669,29 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line',
group group
) )
TIPS = OrderedDict([
('time','@ftime'),
('pace','@fpace'),
('hr','@hr'),
('spm','@spm{1.1}'),
('distance','@distance{5}'),
])
hover = plot.select(type=HoverTool)
hover.tooltips = TIPS
if labeldict: if labeldict:
legend=labeldict[id] legend=labeldict[id]
else: else:
legend=str(id) legend=str(id)
if plottype=='line': if plottype=='line':
plot.line('x','y',source=source,color=color,legend=legend) l1 = plot.line('x','y',source=source,color=color,legend=legend)
else: else:
plot.scatter('x','y',source=source,color=color,legend=legend, l1 = plot.scatter('x','y',source=source,color=color,legend=legend,
fill_alpha=0.4,line_color=None) fill_alpha=0.4,line_color=None)
plot.add_tools(HoverTool(renderers=[l1],tooltips=TIPS))
cntr += 1 cntr += 1
plot.legend.location='bottom_right' plot.legend.location='bottom_right'
@@ -1725,7 +1733,7 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line',
def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm', def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
promember=0,plottype='line'): promember=0,plottype='line'):
@@ -1743,8 +1751,8 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
rowdata2 = dataprep.getsmallrowdata_db(columns,ids=[id2]) rowdata2 = dataprep.getsmallrowdata_db(columns,ids=[id2])
row1 = Workout.objects.get(id=id1) row1 = Workout.objects.get(id=id1)
row2 = Workout.objects.get(id=id2) row2 = Workout.objects.get(id=id2)
if rowdata1.empty: if rowdata1.empty:
return "","CSV Data File Not Found" return "","CSV Data File Not Found"
else: else:
@@ -1797,32 +1805,39 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
data = pd.DataFrame( data1 = pd.DataFrame(
dict( dict(
x1=x1, x1=x1,
x2=x2,
y1=y1, y1=y1,
y2=y2,
ftime1=ftime1, ftime1=ftime1,
ftime2=ftime2,
fpace1=fpace1, fpace1=fpace1,
fpace2=fpace2,
hr1 = hr1, hr1 = hr1,
hr2 = hr2,
spm1 = spm1, spm1 = spm1,
spm2 = spm2,
distance1=distance1, distance1=distance1,
)
).dropna()
data2 = pd.DataFrame(
dict(
x2=x2,
y2=y2,
ftime2=ftime2,
fpace2=fpace2,
hr2 = hr2,
spm2 = spm2,
distance2=distance2, distance2=distance2,
) )
).dropna() ).dropna()
source = ColumnDataSource(
data source1 = ColumnDataSource(
data1
) )
source2 = ColumnDataSource(
data2
)
# create interactive plot # create interactive plot
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
@@ -1830,22 +1845,52 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
plot_width=920, plot_width=920,
toolbar_sticky=False) toolbar_sticky=False)
TIPS = OrderedDict([
('time','@ftime1'),
('pace','@fpace1'),
('hr','@hr1'),
('spm','@spm1{1.1}'),
('distance','@distance1{5}'),
])
TIPS2 = OrderedDict([
('time','@ftime2'),
('pace','@fpace2'),
('hr','@hr2'),
('spm','@spm2{1.1}'),
('distance','@distance2{5}'),
])
hover1 = plot.select(type=HoverTool)
hover1.tooltips = TIPS
hover2 = plot.select(type=HoverTool)
hover2.tooltips = TIPS2
if plottype=='line': if plottype=='line':
plot.line('x1','y1',source=source,color="blue",legend=row1.name) l1 = plot.line('x1','y1',source=source1,
plot.line('x2','y2',source=source,color="red",legend=row2.name) color="blue",legend=row1.name,
)
l2 = plot.line('x2','y2',source=source2,
color="red",legend=row2.name,
)
elif plottype=='scatter': elif plottype=='scatter':
plot.scatter('x1','y1',source=source,legend=row1.name,fill_alpha=0.4, l1 = plot.scatter('x1','y1',source=source1,legend=row1.name,
line_color=None) fill_alpha=0.4,
plot.scatter('x2','y2',source=source,legend=row2.name,fill_alpha=0.4, line_color=None)
line_color=None,color="red") l2 = plot.scatter('x2','y2',source=source2,legend=row2.name,
fill_alpha=0.4,
line_color=None,color="red")
plot.add_tools(HoverTool(renderers=[l1],tooltips=TIPS))
plot.add_tools(HoverTool(renderers=[l2],tooltips=TIPS2))
plot.legend.location = "bottom_right" plot.legend.location = "bottom_right"
plot.title.text = row1.name+' vs '+row2.name plot.title.text = row1.name+' vs '+row2.name
plot.title.text_font_size=value("1.2em") plot.title.text_font_size=value("1.2em")
plot.xaxis.axis_label = axlabels[xparam] plot.xaxis.axis_label = axlabels[xparam]
plot.yaxis.axis_label = axlabels[yparam]
if xparam == 'time': if xparam == 'time':
plot.xaxis[0].formatter = DatetimeTickFormatter( plot.xaxis[0].formatter = DatetimeTickFormatter(
@@ -1863,27 +1908,10 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
seconds = ["%S"], seconds = ["%S"],
minutes = ["%M"] minutes = ["%M"]
) )
plot.y_range = Range1d(ymin,ymax) plot.y_range = Range1d(ymin,ymax)
#plot.y_range = Range1d(ymin,ymax)
hover = plot.select(dict(type=HoverTool))
hover.tooltips = OrderedDict([
('time1','@ftime1'),
('time2','@ftime2'),
('pace1','@fpace1'),
('pace2','@fpace2'),
('hr1','@hr1'),
('hr2','@hr2'),
('spm1','@spm1{1.1}'),
('spm2','@spm2{1.1}'),
('distance1','@distance1{5}'),
('distance2','@distance2{5}'),
])
hover.mode = 'mouse'
script, div = components(plot) script, div = components(plot)
return [script,div] return [script,div]

View File

@@ -891,6 +891,7 @@ class WorkoutComment(models.Model):
comment = models.TextField(max_length=300) comment = models.TextField(max_length=300)
created = models.DateTimeField(default=timezone.now) created = models.DateTimeField(default=timezone.now)
read = models.BooleanField(default=False) read = models.BooleanField(default=False)
notification = models.BooleanField(default=False,verbose_name="Send me notifications")
user = models.ForeignKey(User) user = models.ForeignKey(User)
workout = models.ForeignKey(Workout) workout = models.ForeignKey(Workout)
@@ -905,7 +906,7 @@ class WorkoutComment(models.Model):
class WorkoutCommentForm(ModelForm): class WorkoutCommentForm(ModelForm):
class Meta: class Meta:
model = WorkoutComment model = WorkoutComment
fields = ['comment',] fields = ['comment','notification']
widgets = { widgets = {
'comment': forms.Textarea, 'comment': forms.Textarea,
} }

View File

@@ -273,6 +273,37 @@ def handle_sendemail_invite(email,name,code,teamname,manager):
return 1 return 1
@app.task
def handle_sendemailnewresponse(first_name,last_name,
email,
commenter_first_name,
commenter_last_name,
comment,
workoutname,workoutid,commentid):
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 the 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'
message += '\n\n'
message += 'You are receiving this email because you are subscribed '
message += 'to comments on this workout. To unsubscribe, follow this link:\n'
message += 'https://rowsandall.com/rowers/workout/unsubscribe/'+str(workoutid)+'/'
email = EmailMessage(subject, message,
'Rowsandall <info@rowsandall.com>',
[fullemail])
res = email.send()
return 1
@app.task @app.task
def handle_sendemailnewcomment(first_name, def handle_sendemailnewcomment(first_name,
last_name, last_name,

View File

@@ -9,7 +9,7 @@
<div class="grid_12"> <div class="grid_12">
<div class="grid_8 suffix_4"> <div class="grid_8 suffix_4">
{% include "teambuttons.html" with teamid=teamid%} {% include "teambuttons.html" with teamid=team.id %}
</div> </div>
</div> </div>
<div class="grid_12 alpha"> <div class="grid_12 alpha">

View File

@@ -57,7 +57,7 @@
{% endfor %} {% endfor %}
<div id="form" class="grid_6 alpha"> <div id="form" class="grid_6 alpha">
<form enctype="multipart/form-data" action="" method="post"> <form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id }}/comment" method="post">
<table width=100%> <table width=100%>
{{ form.as_table }} {{ form.as_table }}
</table> </table>

View File

@@ -151,6 +151,7 @@ urlpatterns = [
url(r'^workout/upload/(.+.*)$',views.workout_upload_view), url(r'^workout/upload/(.+.*)$',views.workout_upload_view),
url(r'^workout/(?P<id>\d+)/histo$',views.workout_histo_view), url(r'^workout/(?P<id>\d+)/histo$',views.workout_histo_view),
url(r'^workout/(?P<id>\d+)/forcecurve$',views.workout_forcecurve_view), url(r'^workout/(?P<id>\d+)/forcecurve$',views.workout_forcecurve_view),
url(r'^workout/(?P<id>\d+)/unsubscribe$',views.workout_unsubscribe_view),
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.workout_export_view), url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)/s/(?P<successmessage>\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/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),

View File

@@ -60,7 +60,8 @@ from rest_framework.parsers import JSONParser
from rowers.rows import handle_uploaded_file from rowers.rows import handle_uploaded_file
from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv
from rowers.tasks import ( from rowers.tasks import (
handle_sendemail_unrecognized,handle_sendemailnewcomment handle_sendemail_unrecognized,handle_sendemailnewcomment,
handle_sendemailnewresponse,
) )
from scipy.signal import savgol_filter from scipy.signal import savgol_filter
@@ -2064,10 +2065,6 @@ def multi_compare_view(request):
if result: if result:
promember=1 promember=1
if 'ids' in request.session:
print request.session['ids']
print request.POST
if request.method == 'POST' and 'workouts' in request.POST: if request.method == 'POST' and 'workouts' in request.POST:
form = WorkoutMultipleCompareForm(request.POST) form = WorkoutMultipleCompareForm(request.POST)
@@ -3586,6 +3583,37 @@ def workout_export_view(request,id=0, message="", successmessage=""):
'successmessage':successmessage, 'successmessage':successmessage,
'c2userid':c2userid, 'c2userid':c2userid,
}) })
#
@login_required()
def workout_unsubscribe_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")
comments = WorkoutComment.objects.filter(workout=w,
user=request.user).order_by("created")
for c in comments:
c.notify = False
c.save()
form = WorkoutCommentForm()
message = 'You have been unsubscribed from new comment notifications for this workout'
return render(request,
'workout_comments.html',
{'workout':w,
'comments':comments,
'successmessage':message,
'form':form,
})
# list of comments to a workout # list of comments to a workout
@login_required() @login_required()
@@ -3597,6 +3625,8 @@ def workout_comment_view(request,id=0):
if w.privacy == 'private' and w.user.user != request.user: if w.privacy == 'private' and w.user.user != request.user:
return HttpResponseForbidden("Permission error") return HttpResponseForbidden("Permission error")
comments = WorkoutComment.objects.filter(workout=w).order_by("created")
# ok we're permitted # ok we're permitted
if request.method == 'POST': if request.method == 'POST':
@@ -3604,7 +3634,9 @@ def workout_comment_view(request,id=0):
form = WorkoutCommentForm(request.POST) form = WorkoutCommentForm(request.POST)
if form.is_valid(): if form.is_valid():
cd = form.cleaned_data cd = form.cleaned_data
comment = cd['comment'] comment = cd['comment']
notification = cd['notification']
c = WorkoutComment(workout=w,user=request.user,comment=comment,
notification=notification) notification=notification)
c.save() c.save()
if settings.DEBUG: if settings.DEBUG:
@@ -3615,6 +3647,7 @@ def workout_comment_view(request,id=0):
request.user.last_name, request.user.last_name,
comment,w.name, comment,w.name,
w.id) w.id)
elif request.user != r.user: elif request.user != r.user:
res = queuehigh.enqueue(handle_sendemailnewcomment,r.user.first_name, res = queuehigh.enqueue(handle_sendemailnewcomment,r.user.first_name,
@@ -3622,9 +3655,30 @@ def workout_comment_view(request,id=0):
r.user.email, r.user.email,
request.user.first_name, request.user.first_name,
request.user.last_name, request.user.last_name,
comment,w.name,w.id)
comment,w.name,w.id) comment,w.name,w.id)
commenters = {oc.user for oc in comments if oc.notification}
for u in commenters:
if settings.DEBUG:
res = handle_sendemailnewresponse.delay(u.first_name,
u.last_name,
u.email,
request.user.first_name,
request.user.last_name,
comment,
w.name,
w.id,
c.id)
else:
res = queuelow.enqueue(handle_sendemailnewresponse,
u.first_name,
u.last_name,
u.email,
request.user.first_name,
request.user.last_name,
comment,
w.name,
w.id,
c.id) c.id)
form = WorkoutCommentForm() form = WorkoutCommentForm()

View File

@@ -799,9 +799,10 @@ a.wh:hover {
/* talk bubble contents */ /* talk bubble contents */
.talktext{ .talktext{
padding: 1em; padding: 1em;
text-align: left; text-align: left;
line-height: 1.5em; line-height: 1.5em;
word-wrap: break-word;
} }
.talktext p{ .talktext p{
/* remove webkit p margins */ /* remove webkit p margins */