Merge branch 'feature/multicompare3' into develop
This commit is contained in:
@@ -19,7 +19,7 @@ from bokeh.layouts import layout,widgetbox
|
||||
from bokeh.layouts import row as layoutrow
|
||||
from bokeh.layouts import column as layoutcolumn
|
||||
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 (
|
||||
GMapPlot, GMapOptions, ColumnDataSource, Circle,
|
||||
DataRange1d, PanTool, WheelZoomTool, BoxSelectTool,
|
||||
@@ -1604,10 +1604,6 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line',
|
||||
if datadf.empty:
|
||||
return ['','<p>No non-zero data in selection</p>','','']
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if xparam != 'distance' and xparam != 'time' and xparam != 'cumdist':
|
||||
xaxmax = yaxmaxima[xparam]
|
||||
xaxmin = yaxminima[xparam]
|
||||
@@ -1673,17 +1669,29 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line',
|
||||
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:
|
||||
legend=labeldict[id]
|
||||
else:
|
||||
legend=str(id)
|
||||
|
||||
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:
|
||||
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)
|
||||
|
||||
plot.add_tools(HoverTool(renderers=[l1],tooltips=TIPS))
|
||||
cntr += 1
|
||||
|
||||
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',
|
||||
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])
|
||||
row1 = Workout.objects.get(id=id1)
|
||||
row2 = Workout.objects.get(id=id2)
|
||||
|
||||
|
||||
|
||||
|
||||
if rowdata1.empty:
|
||||
return "","CSV Data File Not Found"
|
||||
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'
|
||||
|
||||
|
||||
data = pd.DataFrame(
|
||||
data1 = pd.DataFrame(
|
||||
dict(
|
||||
x1=x1,
|
||||
x2=x2,
|
||||
y1=y1,
|
||||
y2=y2,
|
||||
ftime1=ftime1,
|
||||
ftime2=ftime2,
|
||||
fpace1=fpace1,
|
||||
fpace2=fpace2,
|
||||
hr1 = hr1,
|
||||
hr2 = hr2,
|
||||
spm1 = spm1,
|
||||
spm2 = spm2,
|
||||
distance1=distance1,
|
||||
)
|
||||
).dropna()
|
||||
|
||||
data2 = pd.DataFrame(
|
||||
dict(
|
||||
x2=x2,
|
||||
y2=y2,
|
||||
ftime2=ftime2,
|
||||
fpace2=fpace2,
|
||||
hr2 = hr2,
|
||||
spm2 = spm2,
|
||||
distance2=distance2,
|
||||
)
|
||||
).dropna()
|
||||
|
||||
|
||||
|
||||
source = ColumnDataSource(
|
||||
data
|
||||
|
||||
source1 = ColumnDataSource(
|
||||
data1
|
||||
)
|
||||
|
||||
|
||||
source2 = ColumnDataSource(
|
||||
data2
|
||||
)
|
||||
|
||||
# create interactive plot
|
||||
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,
|
||||
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':
|
||||
plot.line('x1','y1',source=source,color="blue",legend=row1.name)
|
||||
plot.line('x2','y2',source=source,color="red",legend=row2.name)
|
||||
l1 = plot.line('x1','y1',source=source1,
|
||||
color="blue",legend=row1.name,
|
||||
)
|
||||
l2 = plot.line('x2','y2',source=source2,
|
||||
color="red",legend=row2.name,
|
||||
)
|
||||
elif plottype=='scatter':
|
||||
plot.scatter('x1','y1',source=source,legend=row1.name,fill_alpha=0.4,
|
||||
line_color=None)
|
||||
plot.scatter('x2','y2',source=source,legend=row2.name,fill_alpha=0.4,
|
||||
line_color=None,color="red")
|
||||
l1 = plot.scatter('x1','y1',source=source1,legend=row1.name,
|
||||
fill_alpha=0.4,
|
||||
line_color=None)
|
||||
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.title.text = row1.name+' vs '+row2.name
|
||||
plot.title.text_font_size=value("1.2em")
|
||||
plot.xaxis.axis_label = axlabels[xparam]
|
||||
plot.yaxis.axis_label = axlabels[yparam]
|
||||
|
||||
|
||||
if xparam == 'time':
|
||||
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
||||
@@ -1863,27 +1908,10 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
|
||||
seconds = ["%S"],
|
||||
minutes = ["%M"]
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
return [script,div]
|
||||
|
||||
@@ -891,6 +891,7 @@ class WorkoutComment(models.Model):
|
||||
comment = models.TextField(max_length=300)
|
||||
created = models.DateTimeField(default=timezone.now)
|
||||
read = models.BooleanField(default=False)
|
||||
notification = models.BooleanField(default=False,verbose_name="Send me notifications")
|
||||
user = models.ForeignKey(User)
|
||||
workout = models.ForeignKey(Workout)
|
||||
|
||||
@@ -905,7 +906,7 @@ class WorkoutComment(models.Model):
|
||||
class WorkoutCommentForm(ModelForm):
|
||||
class Meta:
|
||||
model = WorkoutComment
|
||||
fields = ['comment',]
|
||||
fields = ['comment','notification']
|
||||
widgets = {
|
||||
'comment': forms.Textarea,
|
||||
}
|
||||
|
||||
@@ -273,6 +273,37 @@ def handle_sendemail_invite(email,name,code,teamname,manager):
|
||||
|
||||
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
|
||||
def handle_sendemailnewcomment(first_name,
|
||||
last_name,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<div class="grid_12">
|
||||
<div class="grid_8 suffix_4">
|
||||
{% include "teambuttons.html" with teamid=teamid%}
|
||||
{% include "teambuttons.html" with teamid=team.id %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_12 alpha">
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
{% endfor %}
|
||||
|
||||
<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%>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
|
||||
@@ -151,6 +151,7 @@ urlpatterns = [
|
||||
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+)/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+.*)$',views.workout_export_view),
|
||||
url(r'^workout/(?P<id>\d+)/export/s/(?P<successmessage>\w+.*)$',views.workout_export_view),
|
||||
|
||||
@@ -60,7 +60,8 @@ 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,handle_sendemailnewcomment
|
||||
handle_sendemail_unrecognized,handle_sendemailnewcomment,
|
||||
handle_sendemailnewresponse,
|
||||
)
|
||||
|
||||
from scipy.signal import savgol_filter
|
||||
@@ -2064,10 +2065,6 @@ def multi_compare_view(request):
|
||||
if result:
|
||||
promember=1
|
||||
|
||||
if 'ids' in request.session:
|
||||
print request.session['ids']
|
||||
|
||||
print request.POST
|
||||
|
||||
if request.method == 'POST' and 'workouts' in request.POST:
|
||||
form = WorkoutMultipleCompareForm(request.POST)
|
||||
@@ -3586,6 +3583,37 @@ def workout_export_view(request,id=0, message="", successmessage=""):
|
||||
'successmessage':successmessage,
|
||||
'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
|
||||
@login_required()
|
||||
@@ -3597,6 +3625,8 @@ def workout_comment_view(request,id=0):
|
||||
|
||||
if w.privacy == 'private' and w.user.user != request.user:
|
||||
return HttpResponseForbidden("Permission error")
|
||||
|
||||
comments = WorkoutComment.objects.filter(workout=w).order_by("created")
|
||||
|
||||
# ok we're permitted
|
||||
if request.method == 'POST':
|
||||
@@ -3604,7 +3634,9 @@ def workout_comment_view(request,id=0):
|
||||
form = WorkoutCommentForm(request.POST)
|
||||
if form.is_valid():
|
||||
cd = form.cleaned_data
|
||||
comment = cd['comment']
|
||||
comment = cd['comment']
|
||||
notification = cd['notification']
|
||||
c = WorkoutComment(workout=w,user=request.user,comment=comment,
|
||||
notification=notification)
|
||||
c.save()
|
||||
if settings.DEBUG:
|
||||
@@ -3615,6 +3647,7 @@ def workout_comment_view(request,id=0):
|
||||
request.user.last_name,
|
||||
comment,w.name,
|
||||
w.id)
|
||||
|
||||
|
||||
elif request.user != r.user:
|
||||
res = queuehigh.enqueue(handle_sendemailnewcomment,r.user.first_name,
|
||||
@@ -3622,9 +3655,30 @@ def workout_comment_view(request,id=0):
|
||||
r.user.email,
|
||||
request.user.first_name,
|
||||
request.user.last_name,
|
||||
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)
|
||||
|
||||
form = WorkoutCommentForm()
|
||||
|
||||
@@ -799,9 +799,10 @@ a.wh:hover {
|
||||
|
||||
/* talk bubble contents */
|
||||
.talktext{
|
||||
padding: 1em;
|
||||
text-align: left;
|
||||
line-height: 1.5em;
|
||||
padding: 1em;
|
||||
text-align: left;
|
||||
line-height: 1.5em;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.talktext p{
|
||||
/* remove webkit p margins */
|
||||
|
||||
Reference in New Issue
Block a user