Private
Public Access
1
0
Files
rowsandall/rowers/views/workoutviews.py
Sander Roosendaal d96a4962c9 fix #486
2019-06-11 17:17:11 +02:00

5328 lines
169 KiB
Python

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from rowers.views.statements import *
import rowers.teams as teams
# Show the EMpower Oarlock generated Stroke Profile
@user_passes_test(ispromember,login_url="/rowers/paidplans/",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_forcecurve_view(request,id=0,workstrokesonly=False):
row = get_workout(id)
promember=0
mayedit=0
if not request.user.is_anonymous:
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember=1
if request.user == row.user.user:
mayedit=1
if not promember:
return HttpResponseRedirect("/rowers/about/")
if request.method == 'POST':
form = ForceCurveOptionsForm(request.POST)
if form.is_valid():
includereststrokes = form.cleaned_data['includereststrokes']
plottype = form.cleaned_data['plottype']
workstrokesonly = not includereststrokes
else:
workstrokesonly = True
plottype = 'line'
else:
form = ForceCurveOptionsForm()
plottype = 'line'
script,div,js_resources,css_resources = interactive_forcecurve(
[row],
workstrokesonly=workstrokesonly,
plottype=plottype,
)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse('workout_forcecurve_view',kwargs={'id':id}),
'name': 'Empower Force Curve'
}
]
r = getrower(request.user)
return render(request,
'forcecurve_single.html',
{
'the_script':script,
'rower':r,
'form':form,
'workout':row,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'the_div':div,
'js_res': js_resources,
'css_res':css_resources,
'id':id,
'mayedit':mayedit,
'teams':get_my_teams(request.user),
})
# Test asynchronous tasking and messaging
#@login_required()
#def workout_test_task_view(request,id=0):
# row = Workout.objects.get(id=id)
# res = myqueue(queuehigh,addcomment2,request.user.id,row.id)#
#
#
# url = reverse('workout_edit_view',
# kwargs = {
# 'id':int(id),
# })
# return HttpResponseRedirect(url)
# Show Stroke power histogram for a workout
@login_required()
def workout_histo_view(request,id=0):
w = get_workout(id)
promember=0
mayedit=0
if not request.user.is_anonymous:
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember=1
if request.user == w.user.user:
mayedit=1
if not promember:
return HttpResponseRedirect("/rowers/about/")
res = interactive_histoall([w],'power',False)
script = res[0]
div = res[1]
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse('workout_histo_view',kwargs={'id':id}),
'name': 'Histogram'
}
]
return render(request,
'histo_single.html',
{'interactiveplot':script,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'workout':w,
'rower':r,
'the_div':div,
'id':id,
'mayedit':mayedit,
'teams':get_my_teams(request.user),
})
# add a workout manually
@login_required()
def addmanual_view(request):
r = Rower.objects.get(user=request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':reverse('addmanual_view'),
'name': 'Add Manual Entry'
},
]
if request.method == 'POST':
# Form was submitted
form = WorkoutForm(request.POST)
metricsform = MetricsForm(request.POST)
if form.is_valid() and metricsform.is_valid():
# Get values from form
name = form.cleaned_data['name']
if name == '':
name = 'Manual Entry'
date = form.cleaned_data['date']
starttime = form.cleaned_data['starttime']
workouttype = form.cleaned_data['workouttype']
duration = form.cleaned_data['duration']
weightcategory = form.cleaned_data['weightcategory']
adaptiveclass = form.cleaned_data['adaptiveclass']
distance = form.cleaned_data['distance']
notes = form.cleaned_data['notes']
thetimezone = form.cleaned_data['timezone']
private = form.cleaned_data['private']
avghr = metricsform.cleaned_data['avghr']
avgpwr = metricsform.cleaned_data['avgpwr']
avgspm = metricsform.cleaned_data['avgspm']
try:
ps = form.cleaned_data['plannedsession']
except KeyError:
ps = None
try:
boattype = request.POST['boattype']
except KeyError:
boattype = '1x'
try:
privacy = request.POST['privacy']
except KeyError:
privacy = 'visible'
try:
rankingpiece = form.cleaned_data['rankingpiece']
except KeyError:
rankingpiece = False
try:
duplicate = form.cleaned_data['duplicate']
except KeyError:
duplicate = False
if private:
privacy = 'private'
else:
privacy = 'visible'
startdatetime = (str(date) + ' ' + str(starttime))
startdatetime = datetime.datetime.strptime(startdatetime,
"%Y-%m-%d %H:%M:%S")
startdatetime = timezone.make_aware(startdatetime)
startdatetime = startdatetime.astimezone(
pytz.timezone(thetimezone)
)
id,message = dataprep.create_row_df(r,
distance,
duration,startdatetime,
weightcategory=weightcategory,
adaptiveclass=adaptiveclass,
avghr=avghr,
rankingpiece=rankingpiece,
avgpwr=avgpwr,
duplicate=duplicate,
avgspm=avgspm,
title = name,
notes=notes,
workouttype=workouttype)
if message:
messages.error(request,message)
if id:
w = Workout.objects.get(id=id)
w.rankingpiece = rankingpiece
w.privacy = privacy
w.weightcategory = weightcategory
w.adaptiveclass = adaptiveclass
w.notes = notes
w.plannedsession = ps
w.name = name
w.workouttype = workouttype
w.boattype = boattype
w.save()
if ps:
add_workouts_plannedsession([w],ps,w.user)
messages.info(request,'New workout created')
url = reverse(
'workout_edit_view',
kwargs={'id':encoder.encode_hex(id)}
)
return HttpResponseRedirect(url)
else:
return render(request,'manualadd.html',
{'form':form,
'metricsform':metricsform,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
})
initial = {
'workouttype':'rower',
'date':datetime.date.today(),
'starttime':timezone.now(),
'timezone':r.defaulttimezone,
'duration':datetime.timedelta(minutes=2),
'distance':500,
}
form = WorkoutForm(initial=initial)
metricsform = MetricsForm()
return render(request,'manualadd.html',
{'form':form,
'metricsform':metricsform,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
})
@login_required()
def fitness_metric_view(request,mode='rower',days=42):
r = getrower(request.user)
startdate = timezone.now()-datetime.timedelta(days=days)
# test if not something already done
ms = PowerTimeFitnessMetric.objects.filter(user=request.user)
if not ms:
url = reverse('workouts_view')
return HttpResponseRedirect(url)
max_workout_id = max([m.last_workout for m in ms])
last_update_date = max([m.date.strftime('%Y-%m-%d') for m in ms])
now_date = timezone.now().strftime('%Y-%m-%d')
if mode == 'rower':
workouts = Workout.objects.filter(
user=r,
workouttype__in=['rower','dynamic','slides'],
startdatetime__gte=startdate)
else:
workouts = Workout.objects.filter(
user=r,
workouttype__in=['water','coastal'],
startdatetime__gte=startdate)
theids = [int(w.id) for w in workouts]
max_id = max(theids)
if last_update_date >= now_date or max_workout_id >= max_id:
return HttpResponse("already done today or no new workouts")
job = myqueue(queue,
handle_updatefitnessmetric,
request.user.id,mode,theids,
)
return HttpResponse("job queued")
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_update_cp_view(request,id=0):
row = get_workout(id)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
row.rankingpiece = True
row.save()
r = getrower(request.user)
dataprep.runcpupdate(r)
if row.workouttype in mytypes.otwtypes:
url = reverse('otwrankings_view')
else:
url = reverse('oterankings_view')
return HttpResponseRedirect(url)
# Reload the workout and calculate the summary from the stroke data (lapIDx)
@login_required()
def workout_recalcsummary_view(request,id=0):
row = get_workout(id)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
filename = row.csvfilename
rowdata = rdata(filename)
if rowdata:
row.summary = rowdata.allstats()
row.save()
successmessage = "Summary Updated"
messages.info(request,successmessage)
url = reverse('workout_edit_view',
kwargs = {
'id':id,
})
else:
message = "Something went wrong. Could not update summary"
messages.error(request,message)
url = reverse('workout_edit_view',
kwargs = {
'id':id,
})
return HttpResponseRedirect(url)
# Joining workout
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workouts_join_view(request):
promember=0
if not request.user.is_anonymous:
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember=1
if request.method == 'POST' and 'workouts' in request.POST:
form = WorkoutMultipleCompareForm(request.POST)
paramform = WorkoutJoinParamForm(request.POST)
if form.is_valid() and paramform.is_valid():
workout_name = paramform.cleaned_data['workout_name']
set_private = paramform.cleaned_data['set_private']
cd = form.cleaned_data
workouts = cd['workouts']
ids = [int(w.id) for w in workouts]
request.session['ids'] = ids
id,message = dataprep.join_workouts(r,ids,
title=workout_name,
setprivate=set_private)
if message:
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(id),
})
return HttpResponseRedirect(url)
else:
return HttpResponse("form is not valid")
else:
url = reverse('workouts_join_select')
return HttpResponseRedirect(url)
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workouts_join_select(request,
startdatestring="",
enddatestring="",
message='',
successmessage='',
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now()+datetime.timedelta(days=1),
teamid=0):
try:
r = getrower(request.user)
except Rower.DoesNotExist:
raise Http404("Rower doesn't exist")
if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype']
else:
waterboattype = mytypes.waterboattype
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
modalityform = TrendFlexModalForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
if modalityform.is_valid():
modality = modalityform.cleaned_data['modality']
waterboattype = modalityform.cleaned_data['waterboattype']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
request.session['modalities'] = modalities
request.session['waterboattype'] = waterboattype
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
try:
theteam = Team.objects.get(id=teamid)
except Team.DoesNotExist:
theteam = 0
if r.rowerplan == 'basic' and theteam==0:
raise PermissionDenied("Access denied")
if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
workouts = Workout.objects.filter(team=theteam,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
elif theteam and theteam.viewing == 'coachonly':
workouts = Workout.objects.filter(team=theteam,user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes)
else:
theteam = None
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
searchform = SearchForm(initial={'q':query})
else:
searchform = SearchForm()
form = WorkoutMultipleCompareForm()
form.fields["workouts"].queryset = workouts
if theteam:
theid = theteam.id
else:
theid = 0
joinparamform = WorkoutJoinParamForm()
modalityform = TrendFlexModalForm(initial={
'modality':modality,
'waterboattype':waterboattype
})
messages.info(request,successmessage)
messages.error(request,message)
return render(request, 'workout_join_select.html',
{'workouts': workouts,
'dateform':dateform,
'searchform':searchform,
'startdate':startdate,
'enddate':enddate,
'active':'nav-workouts',
'team':theteam,
'form':form,
'joinparamform':joinparamform,
'modalityform':modalityform,
'teams':get_my_teams(request.user),
})
# Team comparison
@user_passes_test(ispromember,login_url='/rowers/paidplans/',
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def team_comparison_select(request,
startdatestring="",
enddatestring="",
message='',
successmessage='',
userid=0,
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now(),
id=0,
teamid=0):
r = getrequestrower(request,userid=userid)
requestrower = getrower(request.user)
request.session.pop('ps',None)
if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype']
else:
waterboattype = mytypes.waterboattype
if 'rankingonly' in request.session:
rankingonly = request.session['rankingonly']
else:
rankingonly = False
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
modalityform = TrendFlexModalForm(request.POST)
if modalityform.is_valid():
modality = modalityform.cleaned_data['modality']
waterboattype = modalityform.cleaned_data['waterboattype']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
if 'rankingonly' in modalityform.cleaned_data:
rankingonly = modalityform.cleaned_data['rankingonly']
else:
rankingonly = False
request.session['modalities'] = modalities
request.session['waterboattype'] = waterboattype
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
modalityform = TrendFlexModalForm(initial={
'modality':modality,
'waterboattype':waterboattype,
'rankingonly':rankingonly,
})
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
try:
theteam = Team.objects.get(id=teamid)
except Team.DoesNotExist:
theteam = 0
if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
workouts = Workout.objects.filter(team=theteam,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
elif theteam and theteam.viewing == 'coachonly':
workouts = Workout.objects.filter(team=theteam,user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes)
else:
theteam = None
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
if rankingonly:
workouts = workouts.exclude(rankingpiece=False)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
searchform = SearchForm(initial={'q':query})
else:
searchform = SearchForm()
if id:
firstworkout = get_workout(id)
if not checkworkoutuserview(request.user,firstworkout):
raise PermissionDenied("You are not allowed to use this workout")
firstworkoutquery = Workout.objects.filter(id=encoder.decode_hex(id))
workouts = firstworkoutquery | workouts
else:
firstworkout = None
form = WorkoutMultipleCompareForm()
form.fields["workouts"].queryset = workouts
if id:
form.fields["workouts"].initial = [firstworkout]
if theteam:
theid = theteam.id
else:
theid = 0
chartform = ChartParamChoiceForm(initial={'teamid':0})
messages.info(request,successmessage)
messages.error(request,message)
if id:
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': firstworkout.name
},
{
'url':reverse('team_comparison_select',kwargs={'id':id,'teamid':teamid}),
'name':'Compare Select'
},
]
else:
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':reverse('team_comparison_select',kwargs={'teamid':teamid}),
'name': 'Compare Select'
},
]
return render(request, 'team_compare_select.html',
{'workouts': workouts,
'workout':firstworkout,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'team':theteam,
'searchform':searchform,
'form':form,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'chartform':chartform,
'modalityform':modalityform,
'teams':get_my_teams(request.user),
})
def virtualevent_compare_view(request,id=0):
results = []
promember = 0
if not request.user.is_anonymous:
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember=1
else:
r = None
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
if race.sessiontype == 'race':
script,div = course_map(race.course)
resultobj = VirtualRaceResult
else:
script = ''
div = ''
resultobj = IndoorVirtualRaceResult
results = resultobj.objects.filter(
race=race,
workoutid__isnull=False,
coursecompleted=True,
).order_by("duration","-distance")
workoutids = [result.workoutid for result in results]
if len(workoutids) == 0:
url = reverse('virtualevent_view',
kwargs={
'id':race.id,
})
messages.info(request,'There are no results to display')
return HttpResponseRedirect(url)
if request.method == 'GET':
xparam = race.sessionmode if race.sessionmode in ['distance','time'] else 'time'
yparam = 'pace'
plottype = 'line'
request.session['ids'] = workoutids
request.session['plottype'] = plottype
request.session['xparam'] = xparam
request.session['yparam'] = yparam
workouts = []
for id in workoutids:
try:
workouts.append(Workout.objects.get(id=id))
except Workout.DoesNotExist:
pass
labeldict = {
int(w.id): w.__str__() for w in workouts
}
chartform = ChartParamChoiceForm(
initial = {
'xparam':xparam,
'yparam':yparam,
'plottype':plottype,
'teamid':0
}
)
elif request.method == 'POST':
chartform = ChartParamChoiceForm(request.POST)
if chartform.is_valid():
xparam = chartform.cleaned_data['xparam']
yparam = chartform.cleaned_data['yparam']
plottype = chartform.cleaned_data['plottype']
teamid = chartform.cleaned_data['teamid']
try:
workoutids = request.session['ids']
except KeyError:
pass
request.session['ids'] = workoutids
workouts = []
for id in workoutids:
try:
workouts.append(Workout.objects.get(
id=encoder.decode_hex(id)))
except Workout.DoesNotExist:
pass
labeldict = {
int(w.id): w.__str__() for w in workouts
}
else:
raise HttpResponse('Only GET and POST allowed',status=405)
res = interactive_multiple_compare_chart(workoutids,xparam,yparam,
promember=promember,
plottype=plottype,
labeldict=labeldict)
script = res[0]
div = res[1]
errormessage = res[3]
if errormessage != '':
messages.error(request,errormessage)
breadcrumbs = [
{
'url': reverse('virtualevents_view'),
'name': 'Racing'
},
{
'url':reverse('virtualevent_view',
kwargs={
'id':race.id,
}
),
'name': race.name
},
{
'url':reverse('virtualevent_compare_view',
kwargs={
'id':race.id,
}
),
'name': 'Compare'
}
]
return render(request,'multicompare.html',
{'interactiveplot':script,
'the_div':div,
'breadcrumbs':breadcrumbs,
'rower':r,
'race':race,
'results':results,
'active':'nav-racing',
'promember':promember,
'teamid':0,
'chartform':chartform,
'teams':[]
})
@login_required()
def plannedsession_compare_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
try:
ps = PlannedSession.objects.get(id=id)
except PlannedSession.DoesNotExist:
raise Http404("Planned session does not exist")
m = ps.manager
mm = m.rower
if ps.manager != request.user:
if r.rowerplan == 'coach':
teams = Team.objects.filter(manager=request.user)
members = Rower.objects.filter(team__in=teams).distinct()
teamusers = [m.user for m in members]
if ps.manager not in teamusers:
raise PermissionDenied("You do not have access to this session")
elif r not in ps.rower.all():
raise PermissionDenied("You do not have access to this session")
workouts = Workout.objects.filter(plannedsession=ps)
ids = [int(w.id) for w in workouts]
labeldict = {
int(w.id): w.__str__() for w in workouts
}
xparam = 'time'
yparam = 'hr'
plottype = 'line'
request.session['ids'] = ids
request.session['xparam'] = xparam
request.session['yparam'] = yparam
request.session['plottype'] = plottype
request.session['ps'] = ps.id
if ids:
url = reverse('multi_compare_view',
kwargs={'userid':userid,'id':encoder.encode_hex(ids[0])})
else:
url = reverse('plannedsession_view',kwargs={'id':ps.id})
return HttpResponseRedirect(url)
# Team comparison
@login_required()
def multi_compare_view(request,id=0,userid=0):
promember=0
if not request.user.is_anonymous:
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember=1
if request.method == 'POST' and 'workouts' in request.POST:
form = WorkoutMultipleCompareForm(request.POST)
chartform = ChartParamChoiceForm(request.POST)
if form.is_valid() and chartform.is_valid():
cd = form.cleaned_data
workouts = cd['workouts']
xparam = chartform.cleaned_data['xparam']
yparam = chartform.cleaned_data['yparam']
plottype = chartform.cleaned_data['plottype']
teamid = chartform.cleaned_data['teamid']
ids = [int(w.id) for w in workouts]
request.session['ids'] = ids
labeldict = {
int(w.id): w.__str__() for w in workouts
}
else:
return HttpResponse("Form is not valid")
elif request.method == 'POST' and 'ids' in request.session:
chartform = ChartParamChoiceForm(request.POST)
if chartform.is_valid():
xparam = chartform.cleaned_data['xparam']
yparam = chartform.cleaned_data['yparam']
plottype = chartform.cleaned_data['plottype']
teamid = chartform.cleaned_data['teamid']
ids = request.session['ids']
request.session['ids'] = ids
workouts = []
for id in ids:
try:
workouts.append(Workout.objects.get(id=id))
except Workout.DoesNotExist:
pass
labeldict = {
int(w.id): w.__str__() for w in workouts
}
elif 'ids' in request.session and 'plottype' in request.session:
xparam = request.session['xparam']
yparam = request.session['yparam']
plottype = request.session['plottype']
teamid = 0
ids = request.session['ids']
workouts = []
for id in ids:
try:
workouts.append(Workout.objects.get(id=id))
except Workout.DoesNotExist:
pass
labeldict = {
int(w.id): w.__str__() for w in workouts
}
chartform = ChartParamChoiceForm(
initial = {
'xparam':xparam,
'yparam':yparam,
'plottype':plottype,
'teamid':teamid
}
)
else:
url = reverse('team_comparison_select',
kwargs={
'id':id,
'teamid':0})
return HttpResponseRedirect(url)
res = interactive_multiple_compare_chart(ids,xparam,yparam,
promember=promember,
plottype=plottype,
labeldict=labeldict)
script = res[0]
div = res[1]
errormessage = res[3]
if errormessage != '':
messages.error(request,errormessage)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':reverse('team_comparison_select',kwargs={'teamid':teamid}),
'name': 'Compare Select'
},
{
'url':reverse('multi_compare_view'),
'name': 'Comparison Chart'
}
]
if 'ps' in request.session:
ps = PlannedSession.objects.get(id=int(request.session['ps']))
breadcrumbs = [
{
'url': reverse('plannedsessions_view',
kwargs={'userid':userid}),
'name': 'Sessions'
},
{
'url':reverse('plannedsession_view',
kwargs={
'userid':userid,
'id':ps.id,
}
),
'name': ps.id
},
{
'url':reverse('plannedsession_compare_view',
kwargs={
'userid':userid,
'id':ps.id,
}
),
'name': 'Compare'
}
]
return render(request,'multicompare.html',
{'interactiveplot':script,
'the_div':div,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-workouts',
'promember':promember,
'teamid':teamid,
'chartform':chartform,
'teams':get_my_teams(request.user),
})
# List Workouts
@login_required()
def workouts_view(request,message='',successmessage='',
teamid=0,rankingonly=False,rowerid=0,userid=0):
startdate,enddate = get_dates_timeperiod(request,defaulttimeperiod='lastyear')
request.session['referer'] = absolute(request)['PATH']
r = getrequestrower(request,rowerid=rowerid,userid=userid)
# check if access is allowed
if not checkviewworkouts(request.user,r):
raise PermissionDenied("Access denied")
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
query = None
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
searchform = SearchForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
if searchform.is_valid():
query = searchform.cleaned_data['q']
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
# start date for the small graph
activity_startdate = enddate-datetime.timedelta(days=15)
try:
if utc.localize(enddate) > timezone.now():
activity_enddate = timezone.now()
activity_startdate = activity_enddate-datetime.timedelta(days=15)
else:
activity_enddate = enddate
except ValueError:
activity_enddate = enddate
g_startdate = activity_startdate
g_enddate = activity_enddate
if teamid:
try:
theteam = Team.objects.get(id=teamid)
except Team.DoesNotExist:
raise Http404("Team doesn't exist")
if theteam.viewing == 'allmembers' or theteam.manager == request.user:
workouts = Workout.objects.filter(
team=theteam,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
privacy='visible').order_by("-date","-starttime")
g_workouts = Workout.objects.filter(
team=theteam,
startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate,
duplicate=False,
privacy='visible').order_by("-date", "-starttime")
elif theteam.viewing == 'coachonly':
workouts = Workout.objects.filter(
team=theteam,user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
privacy='visible').order_by("-startdatetime")
g_workouts = Workout.objects.filter(
team=theteam,user=r,
startdatetime__gte=activity_startdate,
enddatetime__lte=activity_enddate,
duplicate=False,
privacy='visible').order_by("-startdatetime")
elif request.user != r.user:
theteam = None
workouts = Workout.objects.filter(
user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
privacy='visible').order_by("-date", "-starttime")
g_workouts = Workout.objects.filter(
user=r,
startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate,
duplicate=False,
privacy='visible').order_by("-startdatetime")
else:
theteam = None
workouts = Workout.objects.filter(
user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by("-date", "-starttime")
g_workouts = Workout.objects.filter(
user=r,
duplicate=False,
startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate).order_by("-startdatetime")
if len(g_workouts) == 0:
g_workouts = Workout.objects.filter(
user=r,
startdatetime__gte=timezone.now()-timedelta(days=15)).order_by("-startdatetime")
g_enddate = timezone.now()
g_startdate = (timezone.now()-timedelta(days=15))
if rankingonly:
workouts = workouts.exclude(rankingpiece=False)
workoutsnohr = workouts.exclude(averagehr__isnull=False)
for w in workoutsnohr:
res = dataprep.workout_trimp(w)
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
searchform = SearchForm(initial={'q':query})
else:
searchform = SearchForm()
paginator = Paginator(workouts,20) # show 25 workouts per page
page = request.GET.get('page')
try:
workouts = paginator.page(page)
except PageNotAnInteger:
workouts = paginator.page(1)
except EmptyPage:
workouts = paginator.page(paginator.num_pages)
today = timezone.now()
announcements = SiteAnnouncement.objects.filter(
expires__gte=today
).order_by(
"-created",
"-id"
)
if theteam:
stack='rower'
else:
stack='type'
script,div = interactive_activitychart(g_workouts,
g_startdate,
g_enddate,
stack=stack)
messages.info(request,successmessage)
messages.error(request,message)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
]
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
return render(request, 'list_workouts.html',
{'workouts': workouts,
'active': 'nav-workouts',
'rower':r,
'searchform':searchform,
'breadcrumbs':breadcrumbs,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'announcements':announcements[0:4],
'team':theteam,
'rankingonly':rankingonly,
'teams':get_my_teams(request.user),
'interactiveplot':script,
'the_div':div,
'timeperiod':timeperiod,
})
# List of workouts to compare a selected workout to
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_fusion_list(request,id=0,message='',successmessage='',
startdatestring="",enddatestring="",
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now()):
try:
r = getrower(request.user)
except Rower.DoesNotExist:
raise Http404("User has no rower instance")
u = User.objects.get(id=r.user.id)
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
if id:
theid = encoder.decode_hex(id)
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=theid)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
searchform = SearchForm(initial={'q':query})
else:
searchform = SearchForm()
paginator = Paginator(workouts,15) # show 25 workouts per page
page = request.GET.get('page')
try:
workouts = paginator.page(page)
except PageNotAnInteger:
workouts = paginator.page(1)
except EmptyPage:
workouts = paginator.page(paginator.num_pages)
row = get_workout(id)
messages.info(request,successmessage)
messages.error(request,message)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,encoder.encode_hex(row.id)),
'name': row.name
},
{
'url':reverse('workout_fusion_list',kwargs={'id':id}),
'name': 'Sensor Fusion'
}
]
return render(request, 'fusion_list.html',
{'id':id,
'workout':row,
'rower':r,
'searchform':searchform,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'workouts': workouts,
'last_name':u.last_name,
'first_name':u.first_name,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'teams':get_my_teams(request.user),
})
# Basic view of workout
def workout_view(request,id=0):
request.session['referer'] = absolute(request)['PATH']
if not request.user.is_anonymous:
rower = getrower(request.user)
else:
rower = None
try:
row = Workout.objects.get(id=encoder.decode_hex(id))
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
comments = WorkoutComment.objects.filter(workout=row)
aantalcomments = len(comments)
if row.privacy == 'private' and not checkworkoutuser(request.user,row):
raise PermissionDenied("Access denied")
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
# create interactive plot
res = interactive_chart(encoder.decode_hex(id))
script = res[0]
div = res[1]
# create map
f1 = row.csvfilename
rowdata = rdata(f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
if not latitude.std():
hascoordinates = 0
except (KeyError,AttributeError):
hascoordinates = 0
else:
hascoordinates = 0
if hascoordinates:
mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
else:
mapscript = ""
mapdiv = ""
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':reverse('workout_view',kwargs={'id':id}),
'name': row.name,
}
]
u = row.user.user
recordsindoor = IndoorVirtualRaceResult.objects.filter(workoutid= row.id)
records = VirtualRaceResult.objects.filter(workoutid= row.id)
return render(request, 'workout_view.html',
{'workout':row,
'rower':rower,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'graphs':g,
'last_name':u.last_name,
'records':records,
'recordsindoor':recordsindoor,
'first_name':u.first_name,
'interactiveplot':script,
'aantalcomments':aantalcomments,
'mapscript':mapscript,
'mapdiv':mapdiv,
'teams':get_my_teams(request.user),
'the_div':div})
# Resets stroke data to raw data (pace)
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_undo_smoothenpace_view(
request,id=0,message="",successmessage=""
):
row = get_workout(id)
r = getrower(request.user)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
filename = row.csvfilename
row = rdata(filename)
if row == 0:
return HttpResponse("Error: CSV Data File Not Found")
if 'originalvelo' in row.df:
velo = row.df['originalvelo'].values
row.df[' Stroke500mPace (sec/500m)'] = 500./velo
row.write_csv(filename,gzip=True)
dataprep.update_strokedata(encoder.decode_hex(id),row.df)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':id,
}
)
return HttpResponseRedirect(url)
# Data smoothing of pace data
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_smoothenpace_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
previousurl = request.META.get('HTTP_REFERER')
r = getrower(request.user)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
filename = row.csvfilename
row = rdata(filename)
if row == 0:
return HttpResponse("Error: CSV Data File Not Found")
pace = row.df[' Stroke500mPace (sec/500m)'].values
velo = 500./pace
if not 'originalvelo' in row.df:
row.df['originalvelo'] = velo
velo2 = stravastuff.ewmovingaverage(velo,5)
pace2 = 500./abs(velo2)
row.df[' Stroke500mPace (sec/500m)'] = pace2
row.df = row.df.fillna(0)
row.write_csv(filename,gzip=True)
dataprep.update_strokedata(encoder.decode_hex(id),row.df)
messages.info(request,'A smoothening filter was applied to your pace data')
if previousurl:
url = previousurl
else:
url = reverse(r.defaultlandingpage,
kwargs = {
'id':id,
}
)
return HttpResponseRedirect(url)
# Process CrewNerd Summary CSV and update summary
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse('workout_crewnerd_summary_view',kwargs={'id':id}),
'name': 'CrewNerd Summary'
}
]
if request.method == 'POST':
form = CNsummaryForm(request.POST,request.FILES)
if form.is_valid():
f = request.FILES['file']
res = handle_uploaded_file(f)
fname = res[1]
try:
sumd = summarydata(fname)
row.summary = sumd.allstats()
row.save()
os.remove(fname)
successmessage = "CrewNerd summary added"
messages.info(request,successmessage)
url = reverse('workout_edit_view',
kwargs = {
'id':id,
})
return HttpResponseRedirect(url)
except:
try:
os.remove(fname)
except:
pass
message = "Something went wrong (workout_crewnerd_summary_view)"
messages.error(request,message)
url = reverse('workout_edit_view',
kwargs = {
'id':id,
})
return HttpResponseRedirect(url)
else:
return render(request,
"cn_form.html",
{'form':form,
'active':'nav-workouts',
'rower':r,
'workout':row,
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'id':row.id})
else:
form = CNsummaryForm()
return render(request,
"cn_form.html",
{'form':form,
'active':'nav-workouts',
'rower':r,
'workout':row,
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'id':row.id})
# Get weather for given location and date/time
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_downloadwind_view(request,id=0,
airportcode=None,
message="",successmessage=""):
row = get_workout(id)
f1 = row.csvfilename
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
# create bearing
rowdata = rdata(f1)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
try:
bearing = rowdata.df.loc[:,'bearing'].values
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1,gzip=True)
# get wind
try:
avglat = rowdata.df[' latitude'].mean()
avglon = rowdata.df[' longitude'].mean()
avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.loc[:,'TimeStamp (sec)'].iloc[0])
startdatetime = dateutil.parser.parse("{}, {}".format(row.date,
row.starttime))
starttimeunix = int(arrow.get(row.startdatetime).timestamp)
#starttimeunix = int(mktime(startdatetime.utctimetuple()))
avgtime = starttimeunix+avgtime
winddata = get_wind_data(avglat,avglon,avgtime)
windspeed = winddata[0]
windbearing = winddata[1]
message = winddata[2]
if message is not None:
try:
row.notes += "\n"+message
except TypeError:
if message is not None and row.notes is not None:
row.notes += message
row.save()
rowdata.add_wind(windspeed,windbearing)
rowdata.write_csv(f1,gzip=True)
messages.info(request,message)
kwargs = {
'id':id}
url = reverse('workout_wind_view',kwargs=kwargs)
response = HttpResponseRedirect(url)
except KeyError:
message = "No latitude/longitude data"
messages.error(request,message)
kwargs = {
'id':id
}
url = reverse('workout_wind_view',kwargs=kwargs)
response = HttpResponseRedirect(url)
return response
# Get weather for given location and date/time
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def workout_downloadmetar_view(request,id=0,
airportcode=None,
message="",successmessage=""):
row = get_workout(id)
f1 = row.csvfilename
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
# create bearing
rowdata = rdata(f1)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
try:
bearing = rowdata.df.loc[:,'bearing'].values
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1,gzip=True)
# get wind
try:
avglat = rowdata.df[' latitude'].mean()
avglon = rowdata.df[' longitude'].mean()
airportcode = get_airport_code(avglat,avglon)[0]
avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.loc[:,'TimeStamp (sec)'].iloc[0])
startdatetime = dateutil.parser.parse("{}, {}".format(row.date,
row.starttime))
starttimeunix = arrow.get(row.startdatetime).timestamp
#starttimeunix = int(mktime(startdatetime.utctimetuple()))
avgtime = starttimeunix +avgtime
winddata = get_metar_data(airportcode,avgtime)
windspeed = winddata[0]
windbearing = winddata[1]
message = winddata[2]
try:
row.notes += "\n"+message
except TypeError:
if message is not None:
try:
row.notes += message
except TypeError:
pass
row.save()
rowdata.add_wind(windspeed,windbearing)
rowdata.write_csv(f1,gzip=True)
messages.info(request,message)
kwargs = {
'id':id}
url = reverse('workout_wind_view',kwargs=kwargs)
response = HttpResponseRedirect(url)
except KeyError:
message = "No latitude/longitude data"
messages.error(request,message)
kwargs = {
'id':id
}
url = reverse('workout_wind_view',kwargs=kwargs)
response = HttpResponseRedirect(url)
return response
# Show form to update wind data
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def workout_wind_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse('workout_wind_view',kwargs={'id':id}),
'name': 'Wind'
}
]
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
# get data
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
# create bearing
rowdata = rdata(f1)
if row == 0:
return HttpResponse("Error: CSV Data File Not Found")
hascoordinates = 1
try:
latitude = rowdata.df.loc[:,' latitude']
except KeyError:
hascoordinates = 0
if hascoordinates and not latitude.std():
hascoordinates = 0
try:
bearing = rowdata.df.loc[:,'bearing'].values
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1,gzip=True)
if hascoordinates:
avglat = rowdata.df[' latitude'].mean()
avglon = rowdata.df[' longitude'].mean()
airportcode,newlat,newlon,airportdistance = get_airport_code(avglat,avglon)
airportcode = airportcode.upper()
airportdistance = airportdistance[0]
else:
airportcode = 'UNKNOWN'
airportdistance = 0
if request.method == 'POST':
# process form
form = UpdateWindForm(request.POST)
if form.is_valid():
vwind1 = form.cleaned_data['vwind1']
vwind2 = form.cleaned_data['vwind2']
dist1 = form.cleaned_data['dist1']
dist2 = form.cleaned_data['dist2']
winddirection1 = form.cleaned_data['winddirection1']
winddirection2 = form.cleaned_data['winddirection2']
windunit = form.cleaned_data['windunit']
rowdata.update_wind(vwind1,vwind2,
winddirection1,
winddirection2,
dist1,dist2,
units=windunit)
rowdata.write_csv(f1,gzip=True)
else:
message = "Invalid Form"
messages.error(request,message)
kwargs = {
'id':id
}
url = reverse('workout_wind_view',kwargs=kwargs)
response = HttpResponseRedirect(url)
else:
form = UpdateWindForm()
# create interactive plot
res = interactive_windchart(encoder.decode_hex(id),promember=1)
script = res[0]
div = res[1]
if hascoordinates:
gmscript,gmdiv = leaflet_chart(
rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
else:
gmscript = ""
gmdiv = "No GPS data available"
messages.info(request,successmessage)
messages.error(request,message)
return render(request,
'windedit.html',
{'workout':row,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
'interactiveplot':script,
'form':form,
'airport':airportcode,
'airportdistance':airportdistance,
'the_div':div,
'gmap':gmscript,
'gmapdiv':gmdiv})
# Show form to update River stream data (for river dwellers)
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def workout_stream_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
r = getrower(request.user)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
# create interactive plot
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
rowdata = rdata(f1)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
if request.method == 'POST':
# process form
form = UpdateStreamForm(request.POST)
if form.is_valid():
dist1 = form.cleaned_data['dist1']
dist2 = form.cleaned_data['dist2']
stream1 = form.cleaned_data['stream1']
stream2 = form.cleaned_data['stream2']
streamunit = form.cleaned_data['streamunit']
rowdata.update_stream(stream1,stream2,dist1,dist2,
units=streamunit)
rowdata.write_csv(f1,gzip=True)
else:
message = "Invalid Form"
messages.error(request,message)
kwargs = {
'id':id}
url = reverse('workout_wind_view',kwargs=kwargs)
response = HttpResponseRedirect(url)
else:
form = UpdateStreamForm()
# create interactive plot
res = interactive_streamchart(encoder.decode_hex(id),promember=1)
script = res[0]
div = res[1]
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse('workout_stream_view',kwargs={'id':id}),
'name': 'Stream'
}
]
messages.info(request,successmessage)
messages.error(request,message)
return render(request,
'streamedit.html',
{'workout':row,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
'interactiveplot':script,
'form':form,
'the_div':div})
# Form to set average crew weight and boat type, then run power calcs
@user_passes_test(ispromember, login_url="/rowers/paidplans",redirect_field_name=None)
def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
w = get_workout(id)
r = getrower(request.user)
mayedit = 0
if request.user == w.user.user:
mayedit=1
if checkworkoutuser(request.user,w):
mayedit=1
if (checkworkoutuser(request.user,w)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
if request.method == 'POST':
# process form
form = AdvancedWorkoutForm(request.POST)
if form.is_valid():
quick_calc = form.cleaned_data['quick_calc']
boattype = form.cleaned_data['boattype']
weightvalue = form.cleaned_data['weightvalue']
w.boattype = boattype
w.weightvalue = weightvalue
w.save()
# load row data & create power/wind/bearing columns if not set
f1 = w.csvfilename
rowdata = rdata(f1)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
try:
vstream = rowdata.df['vstream']
except KeyError:
rowdata.add_stream(0)
rowdata.write_csv(f1,gzip=True)
try:
bearing = rowdata.df['bearing']
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1,gzip=True)
try:
vwind = rowdata.df['vwind']
except KeyError:
rowdata.add_wind(0,0)
rowdata.write_csv(f1,gzip=True)
# do power calculation (asynchronous)
r = w.user
u = r.user
first_name = u.first_name
last_name = u.last_name
emailaddress = u.email
job = myqueue(queuelow,
handle_otwsetpower,f1,boattype,
weightvalue,
first_name,last_name,emailaddress,encoder.decode_hex(id),
ps=[r.p0,r.p1,r.p2,r.p3],
ratio=r.cpratio,
quick_calc = quick_calc,
emailbounced = r.emailbounced
)
try:
request.session['async_tasks'] += [(job.id,'otwsetpower')]
except KeyError:
request.session['async_tasks'] = [(job.id,'otwsetpower')]
successmessage = 'Your calculations have been submitted. You will receive an email when they are done. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>'
messages.info(request,successmessage)
kwargs = {
'id':id}
try:
url = request.session['referer']
except KeyError:
url = reverse('workout_edit_view',kwargs=kwargs)
response = HttpResponseRedirect(url)
return response
else:
message = "Invalid Form"
messages.error(request,message)
kwargs = {
'id':id}
url = reverse('workout_otwsetpower_view',kwargs=kwargs)
response = HttpResponseRedirect(url)
else:
form = AdvancedWorkoutForm(instance=w)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse('workout_otwsetpower_view',kwargs={'id':id}),
'name': 'OTW Power'
}
]
messages.error(request,message)
messages.info(request,successmessage)
return render(request,
'otwsetpower.html',
{'workout':w,
'rower':w,
'mayedit':mayedit,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'form':form,
})
@login_required()
def instroke_view(request,id=0):
w = get_workout(id)
r = getrower(request.user)
mayedit = 0
if request.user == w.user.user:
mayedit=1
if checkworkoutuser(request.user,w):
mayedit=1
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse('instroke_view',kwargs={'id':id}),
'name': 'In-Stroke Metrics'
}
]
# form = WorkoutForm(instance=row)
g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime")
# check if user is owner of this workout
if (checkworkoutuser(request.user,w)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
rowdata = rrdata(csvfile=w.csvfilename)
try:
instrokemetrics = rowdata.get_instroke_columns()
instrokemetrics = [m for m in instrokemetrics if not m in nometrics]
except AttributeError:
instrokemetrics = []
return render(request,
'instroke.html',
{'workout':w,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'mayedit':mayedit,
'teams':get_my_teams(request.user),
'instrokemetrics':instrokemetrics,
})
# generate instroke chart
@login_required()
def instroke_chart(request,id=0,metric=''):
w = get_workout(id)
if (checkworkoutuser(request.user,w)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
rowdata = rrdata(csvfile=w.csvfilename)
instrokemetrics = rowdata.get_instroke_columns()
if metric in instrokemetrics:
f1 = w.csvfilename[6:-4]
timestr = strftime("%Y%m%d-%H%M%S")
imagename = f1+timestr+'.png'
fullpathimagename = 'static/plots/'+imagename
u = w.user.user
r = getrower(u)
title = w.name
fig1 = rowdata.get_plot_instroke(metric)
canvas = FigureCanvas(fig1)
canvas.print_figure('static/plots/'+imagename)
plt.close(fig1)
fig1.clf()
gc.collect()
try:
width,height = Image.open(fullpathimagename).size
except:
width = 1200
height = 600
imgs = GraphImage.objects.filter(workout=w)
if len(imgs) < 7:
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename=fullpathimagename,
width=width,height=height)
i.save()
else:
messages.error(request,'You have reached the maximum number of static images for this workout. Delete an image first')
r = getrower(request.user)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':id,
})
return HttpResponseRedirect(url)
# data explorer
@login_required()
def workout_data_view(request, id=0):
r = getrower(request.user)
w = get_workout(id)
if not checkworkoutuser(request.user,w):
raise PermissionDenied('Access Denied')
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse('workout_data_view',kwargs={'id':id}),
'name': 'Data Explorer'
}
]
datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id))
datadf.sort_values(['ftime'],inplace=True)
columns = datadf.columns.values
to_be_dropped = [
'id','time','hr_an','hr_at','hr_bottom','hr_max',
'hr_tr','hr_ut1','hr_ut2','x_right',
]
to_be_dropped = [c for c in to_be_dropped if c in columns]
datadf.drop(labels=to_be_dropped,inplace=True,axis=1)
cols = ['ftime','cumdist','fpace','spm',
'hr','power','driveenergy','drivelength','averageforce',
'peakforce','distance','drivespeed','workoutstate',
'catch','finish','peakforceangle','wash','slip','rhythm',
'effectiveangle','totalangle','distanceperstroke','velo']
tcols = ['ftime','cumdist','fpace','spm','hr','power']
datadf = datadf[cols]
datadf.loc[:,'hr'] = datadf['hr'].astype('int')
datadf.loc[:,'power'] = datadf['power'].astype('int')
datadf.loc[:,'distance'] = datadf['distance'].astype('int')
datadf.loc[:,'spm'] = 10*datadf['spm'].astype('int')/10.
if request.method == 'POST':
form = DataFrameColumnsForm(request.POST)
if form.is_valid():
tcols = form.cleaned_data['cols']
else:
form = DataFrameColumnsForm(initial = {'cols':tcols})
datadf = datadf[tcols]
for col in cols:
try:
if datadf[col].mean() == 0 and datadf[col].std() == 0:
datadf.drop(labels=[col],axis=1,inplace=True)
except (TypeError,KeyError):
pass
# pd.set_option('display.width', 1000)
#pd.set_option('colheader_justify', 'left')
htmltable = datadf.to_html(
bold_rows=True,
show_dimensions=True,border=1,
classes=['pandastable'],justify='justify'
)
return render(request,
'workout_data.html',
{
'htmltable': htmltable,
'form':form,
'teams':get_my_teams(request.user),
'workout': w,
'breadcrumbs': breadcrumbs,
}
)
# Stats page
@login_required()
def workout_stats_view(request,id=0,message="",successmessage=""):
r = getrower(request.user)
w = get_workout(id)
mayedit = 0
if request.user == w.user.user:
mayedit=1
if checkworkoutuser(request.user,w):
mayedit=1
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse('workout_stats_view',kwargs={'id':id}),
'name': 'Stats'
}
]
workstrokesonly = True
if request.method == 'POST' and 'workstrokesonly' in request.POST:
workstrokesonly = str2bool(request.POST['workstrokesonly'])
# prepare data frame
datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id))
if (checkworkoutuserview(request.user,row)==False):
raise PermissionDenied('Access Denied')
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
if datadf.empty:
datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id))
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=False)
workstrokesonly=False
if datadf.empty:
return HttpResponse("CSV data file not found")
#datadf['deltat'] = datadf['time'].diff()
workoutstateswork = [1,4,5,8,9,6,7]
workoutstatesrest = [3]
workoutstatetransition = [0,2,10,11,12,13]
# Create stats
stats = {}
fieldlist,fielddict = dataprep.getstatsfields()
fielddict.pop('workoutstate')
fielddict.pop('workoutid')
for field,verbosename in fielddict.items():
thedict = {
'mean':datadf[field].mean(),
'wmean': wavg(datadf, field, 'deltat'),
'min': datadf[field].min(),
'std': datadf[field].std(),
'max': datadf[field].max(),
'median': datadf[field].median(),
'firstq':datadf[field].quantile(q=0.25),
'thirdq':datadf[field].quantile(q=0.75),
'verbosename':verbosename,
}
stats[field] = thedict
# Create a dict with correlation values
cor = datadf.corr(method='spearman')
cor.fillna(value=0,inplace=True)
cordict = {}
for field1,verbosename in fielddict.items():
thedict = {}
for field2,verbosename in fielddict.items():
try:
thedict[field2] = cor.loc[field1,field2]
except KeyError:
thedict[field2] = 0
cordict[field1] = thedict
# additional non-automated stats
otherstats = {}
# Normalized power & TSS
tss,normp = dataprep.workout_rscore(w)
if not np.isnan(tss) and tss != 0:
otherstats['tss'] = {
'verbose_name':'rScore',
'value':int(tss),
'unit':''
}
if not np.isnan(normp):
otherstats['np'] = {
'verbose_name':'rPower',
'value':int(10*normp)/10.,
'unit':'Watt'
}
# HR Drift
tmax = datadf['time'].max()
tmin = datadf['time'].min()
thalf = tmin+0.5*(tmax-tmin)
mask1 = datadf['time'] < thalf
mask2 = datadf['time'] > thalf
hr1 = datadf.loc[mask1,'hr'].mean()
hr2 = datadf.loc[mask2,'hr'].mean()
pwr1 = datadf.loc[mask1,'power'].mean()
pwr2 = datadf.loc[mask2,'power'].mean()
try:
hrdrift = ((pwr1/hr1)-(pwr2/hr2))/(pwr1/hr1)
hrdrift *= 100.
if not np.isnan(hrdrift):
hrdrift = int(100*hrdrift)/100.
otherstats['hrdrift'] = {
'verbose_name': 'Heart Rate Drift',
'value': hrdrift,
'unit': '%',
}
except (ZeroDivisionError,ValueError):
pass
# TRIMP
trimp,hrtss = dataprep.workout_trimp(w)
otherstats['trimp'] = {
'verbose_name': 'TRIMP',
'value': trimp,
'unit': ''
}
otherstats['hrScore'] = {
'verbose_name': 'rScore (HR)',
'value': hrtss,
'unit':''
}
return render(request,
'workoutstats.html',
{
'stats':stats,
'teams':get_my_teams(request.user),
'workout':w,
'rower':r,
'mayedit':mayedit,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'workstrokesonly':workstrokesonly,
'cordict':cordict,
'otherstats':otherstats,
})
# Change default landing page
@login_required()
def workflow_default_view(request):
r = getrower(request.user)
if r.defaultlandingpage == 'workout_edit_view':
r.defaultlandingpage = 'workout_workflow_view'
else:
r.defaultlandingpage = 'workout_edit_view'
r.save()
url = reverse('workout_workflow_config2_view')
return HttpResponseRedirect(url)
# Workflow configuration
@login_required()
def workout_workflow_config2_view(request,userid=0):
request.session['referer'] = absolute(request)['PATH']
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
try:
workoutid = request.session['lastworkout']
except KeyError:
workoutid = 0
r = getrequestrower(request,userid=userid,notpermanent=True)
MiddlePanelFormSet = formset_factory(WorkFlowMiddlePanelElement,extra=1)
LeftPanelFormSet = formset_factory(WorkFlowLeftPanelElement,extra=1)
if request.method == 'POST':
wasmiddle = [1 for key,value in request.POST.items() if 'middlepanel' in key.lower()]
wasleft = [1 for key,valye in request.POST.items() if 'leftpanel' in key.lower()]
if wasmiddle:
middlepanel_formset = MiddlePanelFormSet(request.POST,
prefix='middlepanel')
newmiddlepanel = []
if middlepanel_formset.is_valid():
for form in middlepanel_formset:
value = form.cleaned_data.get('panel')
if value != 'None':
newmiddlepanel.append(value)
newmiddlepanel = [i for i in newmiddlepanel if i != None]
r.workflowmiddlepanel = newmiddlepanel
try:
r.save()
except IntegrityError:
messages.error(request,'Something went wrong')
if wasleft:
leftpanel_formset = LeftPanelFormSet(request.POST,
prefix='leftpanel')
newleftpanel = []
if leftpanel_formset.is_valid():
for form in leftpanel_formset:
value = form.cleaned_data.get('panel')
if value != 'None':
newleftpanel.append(value)
newleftpanel = [i for i in newleftpanel if i != None]
r.workflowleftpanel = newleftpanel
try:
r.save()
except IntegrityError:
messages.error(request,'Something went wrong')
leftpanelform_data = [{'panel':panel}
for panel in r.workflowleftpanel]
middlepanelform_data = [{'panel':panel}
for panel in r.workflowmiddlepanel]
leftpanel_formset = LeftPanelFormSet(initial=leftpanelform_data,
prefix='leftpanel')
middlepanel_formset = MiddlePanelFormSet(initial=middlepanelform_data,
prefix='middlepanel')
tmplt = 'workflowconfig2.html'
return render(request,tmplt,
{
'rower':r,
'leftpanel_formset':leftpanel_formset,
'middlepanel_formset':middlepanel_formset,
'workoutid': workoutid,
})
# Workflow View
@login_required()
def workout_workflow_view(request,id):
request.session['referer'] = absolute(request)['PATH']
request.session['lastworkout'] = id
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
row = get_workout_permittedview(request.user,id)
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember=1
if request.user == row.user.user:
mayedit=1
comments = WorkoutComment.objects.filter(workout=row)
aantalcomments = len(comments)
favorites,maxfav = getfavorites(r,row)
charts = get_call()
if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row):
rowdata = rdata(row.csvfilename)
mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
else:
mapscript = ''
mapdiv = ''
statcharts = GraphImage.objects.filter(workout=row)
middleTemplates = []
for t in r.workflowmiddlepanel:
try:
template.loader.get_template(t)
middleTemplates.append(t)
except template.TemplateDoesNotExist:
pass
leftTemplates = []
for t in r.workflowleftpanel:
try:
template.loader.get_template(t)
leftTemplates.append(t)
except template.TemplateDoesNotExist:
pass
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse('workout_workflow_view',kwargs={'id':id}),
'name': 'View'
}
]
return render(request,
'workflow.html',
{
'middleTemplates':middleTemplates,
'leftTemplates':leftTemplates,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'charts':charts,
'workout':row,
'mapscript':mapscript,
'mapdiv':mapdiv,
'statcharts':statcharts,
'rower':r,
'aantalcomments':aantalcomments,
})
# The famous flex chart
@login_required()
def workout_flexchart3_view(request,*args,**kwargs):
try:
id = kwargs['id']
except KeyError:
raise Http404("Invalid workout number")
if 'promember' in kwargs:
promember = kwargs['promember']
else:
promember = 0
try:
favoritenr = int(request.GET['favoritechart'])
except:
favoritenr = -1
row = get_workout(id)
promember=0
mayedit=0
if not request.user.is_anonymous:
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember=1
if request.user == row.user.user:
mayedit=1
if checkworkoutuser(request.user,row):
mayedit=1
workouttype = 'ote'
if row.workouttype in mytypes.otwtypes:
workouttype = 'otw'
favorites,maxfav = getfavorites(r,row)
# check if favoritenr is not out of range
if favorites:
try:
t = favorites[favoritenr].xparam
except IndexError:
favoritenr=0
except AssertionError:
favoritenr=0
if 'xparam' in kwargs:
xparam = kwargs['xparam']
else:
if favorites:
xparam = favorites[favoritenr].xparam
else:
xparam = 'distance'
if 'yparam1' in kwargs:
yparam1 = kwargs['yparam1']
else:
if favorites:
yparam1 = favorites[favoritenr].yparam1
else:
yparam1 = 'pace'
if 'yparam2' in kwargs:
yparam2 = kwargs['yparam2']
if yparam2 == '':
yparam2 = 'None'
else:
if favorites:
yparam2 = favorites[favoritenr].yparam2
if yparam2 == '':
yparam2 = 'None'
else:
yparam2 = 'hr'
if not request.user.is_anonymous:
r = getrower(request.user)
if favoritenr>=0 and r.showfavoritechartnotes:
try:
favoritechartnotes = favorites[favoritenr].notes
except IndexError:
favoritechartnotes = ''
else:
favoritechartnotes = ''
else:
favoritechartnotes = ''
favoritenr = 0
if 'plottype' in kwargs:
plottype = kwargs['plottype']
else:
if favorites:
plottype = favorites[favoritenr].plottype
else:
plottype = 'line'
if 'workstrokesonly' in kwargs:
workstrokesonly = kwargs['workstrokesonly']
else:
if favorites:
workstrokesonly = not favorites[favoritenr].reststrokes
else:
workstrokesonly = False
if request.method == 'POST' and 'savefavorite' in request.POST:
if not request.user.is_anonymous:
workstrokesonly = request.POST['workstrokesonlysave']
reststrokes = not workstrokesonly
r = getrower(request.user)
try:
range = metrics.yaxmaxima[xparam]
if yparam1 is not None:
range = metrics.yaxmaxima[yparam1]
if yparam2 is not None:
range = metrics.yaxmaxima[yparam2]
f = FavoriteChart(user=r,xparam=xparam,
yparam1=yparam1,yparam2=yparam2,
plottype=plottype,workouttype=workouttype,
reststrokes=reststrokes)
f.save()
except KeyError:
messages.error(request,'We cannot save the ad hoc metrics in a favorite chart')
if request.method == 'POST' and 'xaxis' in request.POST:
flexoptionsform = FlexOptionsForm(request.POST)
if flexoptionsform.is_valid():
cd = flexoptionsform.cleaned_data
includereststrokes = cd['includereststrokes']
plottype = cd['plottype']
workstrokesonly = not includereststrokes
flexaxesform = FlexAxesForm(request,request.POST)
if flexaxesform.is_valid():
cd = flexaxesform.cleaned_data
xparam = cd['xaxis']
yparam1 = cd['yaxis1']
yparam2 = cd['yaxis2']
else:
pass
if not promember:
for name,d in rowingmetrics:
if d['type'] != 'basic':
if xparam == name:
xparam = 'time'
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
if yparam1 == name:
yparam1 = 'pace'
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
if yparam2 == name:
yparam2 = 'spm'
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
# bring back slashes
# yparam1 = yparam1.replace('_slsh_','/')
# yparam2 = yparam2.replace('_slsh_','/')
# xparam = xparam.replace('_slsh_','/')
# create interactive plot
try:
(
script,
div,
js_resources,
css_resources,
workstrokesonly
) = interactive_flex_chart2(
encoder.decode_hex(id),xparam=xparam,yparam1=yparam1,
yparam2=yparam2,
promember=promember,plottype=plottype,
workstrokesonly=workstrokesonly
)
except ValueError:
(
script,
div,
js_resources,
css_resources,
workstrokesonly
) = interactive_flex_chart2(
encoder.decode_hex(id),xparam=xparam,yparam1=yparam1,
yparam2=yparam2,
promember=promember,plottype=plottype,
workstrokesonly=workstrokesonly
)
js_resources = ""
css_resources = ""
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
noylist = ["time","distance"]
axchoicesbasic.pop("cumdist")
if row.workouttype in mytypes.otwtypes:
for name,d in rowingmetrics:
if d['mode'] == 'erg':
axchoicespro.pop(name)
else:
for name,d in rowingmetrics:
if d['mode'] == 'water':
axchoicespro.pop(name)
from rowers.metrics import nometrics
rowdata = rdata(row.csvfilename)
try:
rowdata.set_instroke_metrics()
except (AttributeError,TypeError):
pass
try:
additionalmetrics = rowdata.get_additional_metrics()
additionalmetrics = [m for m in additionalmetrics if not m in nometrics]
except AttributeError:
additionalmetrics = []
# extrametrics = {m.replace('/','_slsh_'):m for m in additionalmetrics}
extrametrics = additionalmetrics
# xparam = xparam.replace('/','_slsh_')
# yparam1 = yparam1.replace('/','_slsh_')
# yparam2 = yparam2.replace('/','_slsh_')
# for metric in nometrics:
# try:
# extrametrics.pop(metric)
# except KeyError:
# pass
initial = {
'xaxis':xparam,
'yaxis1':yparam1,
'yaxis2':yparam2,
}
flexaxesform = FlexAxesForm(request,initial=initial,
extrametrics=extrametrics)
initial = {
'includereststrokes': not workstrokesonly,
'plottype':plottype
}
flexoptionsform = FlexOptionsForm(initial=initial)
row = Workout.objects.get(id=encoder.decode_hex(id))
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse('workout_flexchart3_view',kwargs=kwargs),
'name': 'Flex Chart'
}
]
return render(request,
'flexchart3otw.html',
{'the_script':script,
'the_div':div,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-workouts',
'workout':row,
'chartform':flexaxesform,
'optionsform':flexoptionsform,
'js_res': js_resources,
'css_res':css_resources,
'teams':get_my_teams(request.user),
'id':id,
'xparam':xparam,
'yparam1':yparam1,
'yparam2':yparam2,
'plottype':plottype,
'axchoicesbasic':axchoicesbasic,
'axchoicespro':axchoicespro,
'extrametrics':extrametrics,
'favoritechartnotes':favoritechartnotes,
'noylist':noylist,
'mayedit':mayedit,
'promember':promember,
'workstrokesonly': not workstrokesonly,
'favoritenr':favoritenr,
'maxfav':maxfav,
})
# The interactive plot with wind corrected pace for OTW outings
def workout_otwpowerplot_view(request,id=0,message="",successmessage=""):
w = get_workout(id)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse('workout_otwpowerplot_view',kwargs={'id':id}),
'name': 'Interactive OTW Power Plot'
}
]
# check if user is owner of this workout
# create interactive plot
f1 = w.csvfilename
u = w.user.user
# r = getrower(u)
promember=0
mayedit=0
if not request.user.is_anonymous:
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember=1
if request.user == w.user.user:
mayedit=1
# create interactive plot
res = interactive_otw_advanced_pace_chart(encoder.decode_hex(id),promember=promember)
script = res[0]
div = res[1]
messages.error(request,message)
messages.info(request,successmessage)
return render(request,
'otwinteractive.html',
{'workout':w,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'interactiveplot':script,
'the_div':div,
'mayedit':mayedit})
#
@login_required()
def workout_unsubscribe_view(request,id=0):
w = get_workout(id)
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.notification = False
c.save()
form = WorkoutCommentForm()
successmessage = 'You have been unsubscribed from new comment notifications for this workout'
messages.info(request,successmessage)
return render(request,
'workout_comments.html',
{'workout':w,
'teams':get_my_teams(request.user),
'comments':comments,
'form':form,
})
# list of comments to a workout
@login_required()
def workout_comment_view(request,id=0):
w = get_workout(id)
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':
r = w.user
form = WorkoutCommentForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
comment = cd['comment']
comment = bleach.clean(comment)
try:
if isinstance(comment,unicode):
comment = comment.encode('utf8')
elif isinstance(comment, str):
comment = comment.decode('utf8')
except:
pass
notification = cd['notification']
c = WorkoutComment(workout=w,user=request.user,comment=comment,
notification=notification)
c.save()
url = reverse('workout_comment_view',
kwargs={
'id':id,
})
message = '{name} says: <a href="{url}">{comment}</a>'.format(
name = request.user.first_name,
comment = comment,
url = url,
)
if request.user != r.user:
a_messages.info(r.user,message.encode('ascii','ignore'))
res = myqueue(queuehigh,
handle_sendemailnewcomment,r.user.first_name,
r.user.last_name,
r.user.email,
request.user.first_name,
request.user.last_name,
comment,w.name,w.id,
emailbounced = r.emailbounced
)
commenters = {oc.user for oc in comments if oc.notification}
for u in commenters:
a_messages.info(u,message)
if u != request.user and u != r.user:
ocr = Rower.objects.get(user=u)
res = myqueue(queuelow,
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,
emailbounced = ocr.emailbounced
)
url = reverse('workout_comment_view',
kwargs = {
'id':id})
return HttpResponseRedirect(url)
form = WorkoutCommentForm()
g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime")
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
rower = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse('workout_comment_view',kwargs={'id':id}),
'name': 'Comments'
}
]
return render(request,
'workout_comments.html',
{'workout':w,
'rower':rower,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
'graphs':g,
'comments':comments,
'form':form,
})
# The basic edit page
@login_required()
def workout_edit_view(request,id=0,message="",successmessage=""):
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
request.session['referer'] = absolute(request)['PATH']
row = get_workout(id)
if (checkworkoutuser(request.user,row)==False):
raise PermissionDenied("Access denied")
if request.user.rower.rowerplan == 'basic' and 'speedcoach2' in row.workoutsource:
data = getsmallrowdata_db(['wash'],ids=[encoder.decode_hex(id)])
try:
if data['wash'].std() != 0:
url = reverse('paidplans')
messages.info(
request,
'Some Empower Oarlock data are only available to users with a <a href="{u}">paid plan</a>'.format(u=url)
)
except:
pass
form = WorkoutForm(instance=row)
if request.method == 'POST':
# Form was submitted
form = WorkoutForm(request.POST,instance=row)
if form.is_valid():
# Get values from form
name = form.cleaned_data['name']
date = form.cleaned_data['date']
starttime = form.cleaned_data['starttime']
workouttype = form.cleaned_data['workouttype']
weightcategory = form.cleaned_data['weightcategory']
adaptiveclass = form.cleaned_data['adaptiveclass']
duration = form.cleaned_data['duration']
distance = form.cleaned_data['distance']
private = form.cleaned_data['private']
notes = form.cleaned_data['notes']
newdragfactor = form.cleaned_data['dragfactor']
thetimezone = form.cleaned_data['timezone']
try:
ps = form.cleaned_data['plannedsession']
except KeyError:
ps = None
try:
boattype = request.POST['boattype']
except KeyError:
boattype = Workout.objects.get(id=encoder.decode_hex(id)).boattype
try:
privacy = request.POST['privacy']
except KeyError:
privacy = Workout.objects.get(id=row.id).privacy
try:
rankingpiece = form.cleaned_data['rankingpiece']
except KeyError:
rankingpiece =- Workout.objects.get(id=row.id).rankingpiece
try:
duplicate = form.cleaned_data['duplicate']
except KeyError:
duplicate = Workout.objects.get(id=row.id).duplicate
if private:
privacy = 'private'
else:
privacy = 'visible'
startdatetime = datetime.datetime.combine(
date,starttime
)
try:
startdatetime = pytz.timezone(thetimezone).localize(
startdatetime
)
except UnknownTimeZoneError:
pass
try:
# aware object can be in any timezone
out = startdatetime.astimezone(pytz.utc)
except (ValueError, TypeError):
startdatetime = timezone.make_aware(startdatetime)
try:
startdatetime = startdatetime.astimezone(pytz.timezone(thetimezone))
except UnknownTimeZoneError:
thetimezone = 'UTC'
row.name = name
row.date = date
row.starttime = starttime
row.startdatetime = startdatetime
row.workouttype = workouttype
row.weightcategory = weightcategory
row.adaptiveclass = adaptiveclass
row.notes = notes
row.duration = duration
row.distance = distance
row.boattype = boattype
row.duplicate = duplicate
row.privacy = privacy
row.rankingpiece = rankingpiece
row.timezone = thetimezone
row.plannedsession = ps
dragchanged = False
if newdragfactor != row.dragfactor:
row.dragfactor = newdragfactor
dragchanged = True
try:
row.save()
except IntegrityError:
pass
if ps:
add_workouts_plannedsession([row],ps,row.user)
# change data in csv file
r = rdata(row.csvfilename)
if dragchanged:
try:
r.change_drag(newdragfactor)
except AttributeError:
pass
if r == 0:
return HttpResponse("Error: CSV Data File Not Found")
r.rowdatetime = startdatetime
r.write_csv(row.csvfilename,gzip=True)
dataprep.update_strokedata(encoder.decode_hex(id),r.df)
successmessage = "Changes saved"
if rankingpiece:
dataprep.runcpupdate(row.user,type=row.workouttype)
messages.info(request,successmessage)
url = reverse('workout_edit_view',
kwargs = {
'id':encoder.encode_hex(row.id),
})
response = HttpResponseRedirect(url)
#else: # form not POSTed
form = WorkoutForm(instance=row)
row = get_workout(id)
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
# create interactive plot
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
rowdata = rdata(f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
longitude = rowdata.df[' longitude']
if not latitude.std():
hascoordinates = 0
if not longitude.std():
hascoordinates = 0
except (KeyError,AttributeError):
hascoordinates = 0
else:
hascoordinates = 0
mapscript = ""
mapdiv = ""
if hascoordinates:
try:
mapscript,mapdiv = leaflet_chart(
rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
except KeyError:
pass
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,encoder.encode_hex(row.id)),
'name': row.name
},
{
'url':reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(row.id)}),
'name': 'Edit'
}
]
if row.workouttype in mytypes.otetypes:
indoorraces = get_indoorraces(row)
else:
indoorraces = []
r = row.user
# render page
return render(request, 'workout_form.html',
{'form':form,
'workout':row,
'teams':get_my_teams(request.user),
'graphs':g,
'breadcrumbs':breadcrumbs,
'rower':r,
'indoorraces':indoorraces,
'active':'nav-workouts',
'mapscript':mapscript,
'mapdiv':mapdiv,
'rower':r,
})
@login_required()
def workout_map_view(request,id=0):
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
request.session['referer'] = absolute(request)['PATH']
w = get_workout(id)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse('workout_map_view',kwargs={'id':id}),
'name': 'Map'
}
]
# create interactive plot
f1 = w.csvfilename
u = w.user.user
r = getrower(u)
rowdata = rdata(f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
if not latitude.std():
hascoordinates = 0
except (KeyError,AttributeError):
hascoordinates = 0
else:
hascoordinates = 0
if hascoordinates:
mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'],
rowdata.df[' longitude'],
w.name)
else:
mapscript = ""
mapdiv = ""
mayedit=0
if not request.user.is_anonymous:
r = getrower(request.user)
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember=1
if request.user == w.user.user:
mayedit=1
return render(request, 'map_view.html',
{'mapscript':mapscript,
'workout':w,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'mapdiv':mapdiv,
'mayedit':mayedit,
})
# Image upload
@login_required()
def workout_uploadimage_view(request,id):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrower(request.user)
w = get_workout(id)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse('workout_uploadimage_view',kwargs={'id':id}),
'name': 'Upload Image'
}
]
if not checkworkoutuser(request.user,w):
raise PermissionDenied("You are not allowed to edit this workout")
images = GraphImage.objects.filter(workout=w)
if len(images) >= 6:
message = "You have reached the maximum number of static images for this workout"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':id,
})
return HttpResponseRedirect(url)
if request.method == 'POST':
form = ImageForm(request.POST,request.FILES)
if form.is_valid():
f = form.cleaned_data['file']
if f is not None:
filename,path_and_filename = handle_uploaded_image(f)
try:
width,height = Image.open(path_and_filename).size
except:
message = "Not a valid image"
messages.error(request,message)
os.remove(path_and_filename)
url = reverse('workout_uploadimage_view',
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':0,'url':0})
else:
return HttpResponseRedirect(url)
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename = path_and_filename,
width=width,height=height)
i.save()
url = reverse(r.defaultlandingpage,
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':1,'url':url})
else:
return HttpResponseRedirect(url)
else:
messages.error(request,'Something went wrong - no file attached')
url = reverse('workout_uploadimage_view',
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':0,'url':0})
else:
return HttpResponseRedirect(url)
else:
return HttpResponse("Form is not valid")
else:
if not is_ajax:
form = ImageForm()
return render(request,'image_form.html',
{'form':form,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'workout': w,
})
else:
return {'result':0}
# Generic chart creation
@login_required()
def workout_add_chart_view(request,id,plotnr=1):
w = get_workout(id)
r = getrower(request.user)
plotnr = int(plotnr)
if (checkworkoutuser(request.user,w)==False):
raise PermissionDenied("You are not allowed add plots to this workout")
else:
f1 = w.csvfilename[6:-4]
timestr = strftime("%Y%m%d-%H%M%S")
imagename = f1+timestr+'.png'
u = w.user.user
r = getrower(u)
title = w.name
res,jobid = uploads.make_plot(
r,w,f1,w.csvfilename,'timeplot',title,plotnr=plotnr,
imagename=imagename
)
if res == 0:
messages.error(request,jobid)
else:
try:
request.session['async_tasks'] += [(jobid,'make_plot')]
except KeyError:
request.session['async_tasks'] = [(jobid,'make_plot')]
url = reverse(r.defaultlandingpage,kwargs={'id':encoder.encode_hex(w.id)})
return HttpResponseRedirect(url)
@login_required
def workout_toggle_ranking(request,id=0):
is_ajax = False
if request.is_ajax():
is_ajax = True
row = get_workout_permitted(request.user,id)
row.rankingpiece = not row.rankingpiece
row.save()
if is_ajax:
response = JSONResponse({'result':row.rankingpiece},
content_type='application/json')
return response
else:
url = reverse('workouts_view')
response = HttpResponseRedirect(url)
return response
# This is the main view for processing uploaded files
@login_required()
def workout_upload_view(request,
uploadoptions={
'makeprivate':False,
'make_plot':False,
'upload_to_C2':False,
'plottype':'timeplot',
'landingpage':'workout_edit_view',
},
docformoptions={
'workouttype':'rower',
}):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrower(request.user)
if r.rowerplan == 'freecoach':
url = reverse('team_workout_upload_view')
return HttpResponseRedirect(url)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url': reverse('workout_upload_view'),
'name': 'Upload'
}
]
if 'uploadoptions' in request.session:
uploadoptions = request.session['uploadoptions']
try:
defaultlandingpage = uploadoptions['landingpage']
except KeyError:
uploadoptions['landingpage'] = r.defaultlandingpage
defaultlandingpage = r.defaultlandingpage
else:
request.session['uploadoptions'] = uploadoptions
if 'docformoptions' in request.session:
docformoptions = request.session['docformoptions']
else:
request.session['docformoptions'] = docformoptions
try:
makeprivate = uploadoptions['makeprivate']
except KeyError:
makeprivate = False
try:
make_plot = uploadoptions['make_plot']
except KeyError:
make_plot = False
try:
workouttype = docformoptions['workouttype']
except KeyError:
workouttype = 'rower'
try:
boattype = docformoptions['boattype']
except KeyError:
boattype = '1x'
try:
workoutsource = uploadoptions['workoutsource']
except KeyError:
workoutsource = None
try:
plottype = uploadoptions['plottype']
except KeyError:
plottype = 'timeplot'
try:
landingpage = uploadoptions['landingpage']
except KeyError:
landingpage = r.defaultlandingpage
uploadoptions['landingpage'] = landingpage
try:
upload_to_c2 = uploadoptions['upload_to_C2']
except KeyError:
upload_to_c2 = False
try:
upload_to_strava = uploadoptions['upload_to_Strava']
except KeyError:
upload_to_strava = False
try:
upload_to_st = uploadoptions['upload_to_SportTracks']
except KeyError:
upload_to_st = False
try:
upload_to_rk = uploadoptions['upload_to_RunKeeper']
except KeyError:
upload_to_rk = False
try:
upload_to_ua = uploadoptions['upload_to_MapMyFitness']
except KeyError:
upload_to_ua = False
try:
upload_to_tp = uploadoptions['upload_to_TrainingPeaks']
except KeyError:
upload_to_tp = False
response = {}
if request.method == 'POST':
form = DocumentsForm(request.POST,request.FILES)
optionsform = UploadOptionsForm(request.POST,request=request)
if form.is_valid():
# f = request.FILES['file']
f = form.cleaned_data['file']
if f is not None:
res = handle_uploaded_file(f)
else:
messages.error(request,
"Something went wrong - no file attached")
url = reverse('workout_upload_view')
if is_ajax:
return JSONResponse({'result':0,'url':0})
else:
return HttpResponseRedirect(url)
t = form.cleaned_data['title']
workouttype = form.cleaned_data['workouttype']
boattype = form.cleaned_data['boattype']
request.session['docformoptions'] = {
'workouttype':workouttype,
'boattype': boattype,
}
notes = form.cleaned_data['notes']
offline = form.cleaned_data['offline']
race = None
if optionsform.is_valid():
make_plot = optionsform.cleaned_data['make_plot']
plottype = optionsform.cleaned_data['plottype']
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
upload_to_strava = optionsform.cleaned_data['upload_to_Strava']
upload_to_st = optionsform.cleaned_data['upload_to_SportTracks']
upload_to_rk = optionsform.cleaned_data['upload_to_RunKeeper']
upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness']
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
makeprivate = optionsform.cleaned_data['makeprivate']
landingpage = optionsform.cleaned_data['landingpage']
try:
race = optionsform.cleaned_data['submitrace']
except KeyError:
race = None
uploadoptions = {
'makeprivate':makeprivate,
'make_plot':make_plot,
'plottype':plottype,
'upload_to_C2':upload_to_c2,
'upload_to_Strava':upload_to_strava,
'upload_to_SportTracks':upload_to_st,
'upload_to_RunKeeper':upload_to_rk,
'upload_to_MapMyFitness':upload_to_ua,
'upload_to_TrainingPeaks':upload_to_tp,
'landingpage':landingpage,
'boattype': boattype,
'workouttype': workouttype,
}
request.session['uploadoptions'] = uploadoptions
f1 = res[0] # file name
f2 = res[1] # file name incl media directory
if not offline:
id,message,f2 = dataprep.new_workout_from_file(
r,f2,
workouttype=workouttype,
workoutsource=workoutsource,
boattype=boattype,
makeprivate=makeprivate,
title = t,
notes=''
)
else:
workoutsbox = Mailbox.objects.filter(name='workouts')[0]
uploadoptions['fromuploadform'] = True
bodyyaml = yaml.safe_dump(
uploadoptions,
default_flow_style=False
)
msg = Message(mailbox=workoutsbox,
from_header=r.user.email,
subject = t,body=bodyyaml)
msg.save()
f3 = 'media/mailbox_attachments/'+f2[6:]
copyfile(f2,f3)
f3 = f3[6:]
a = MessageAttachment(message=msg,document=f3)
a.save()
os.remove(f2)
messages.info(
request,
"The file was too large to process in real time. It will be processed in a background process. You will receive an email when it is ready")
url = reverse('workout_upload_view')
if is_ajax:
return JSONResponse({'result':1,'url':url})
else:
response = HttpResponseRedirect(url)
return response
if not id:
messages.error(request,message)
url = reverse('workout_upload_view')
if is_ajax:
return JSONResponse({'result':0,'url':url})
else:
response = HttpResponseRedirect(url)
return response
elif id == -1:
message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.'
messages.info(request,message)
url = reverse('workout_upload_view')
if is_ajax:
return JSONResponse({'result':1,'url':url})
else:
response = HttpResponseRedirect(url)
return response
else:
if message:
messages.error(request,message)
w = Workout.objects.get(id=id)
url = reverse('workout_edit_view',
kwargs = {
'id':encoder.encode_hex(w.id),
})
if is_ajax:
response = {'result': 1,'url':url}
else:
response = HttpResponseRedirect(url)
r = getrower(request.user)
if (make_plot):
res,jobid = uploads.make_plot(r,w,f1,f2,plottype,t)
if res == 0:
messages.error(request,jobid)
else:
try:
request.session['async_tasks'] += [(jobid,'make_plot')]
except KeyError:
request.session['async_tasks'] = [(jobid,'make_plot')]
# upload to C2
if (upload_to_c2):
try:
message,id = c2stuff.workout_c2_upload(request.user,w)
except NoTokenError:
id = 0
message = "Something went wrong with the Concept2 sync"
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_strava):
try:
message,id = stravastuff.workout_strava_upload(
request.user,w
)
except NoTokenError:
id = 0
message = "Please connect to Strava first"
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_st):
try:
message,id = sporttracksstuff.workout_sporttracks_upload(
request.user,w
)
except NoTokenError:
message = "Please connect to SportTracks first"
id = 0
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_rk):
try:
message,id = runkeeperstuff.workout_runkeeper_upload(
request.user,w
)
except NoTokenError:
message = "Please connect to Runkeeper first"
id = 0
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_ua):
try:
message,id = underarmourstuff.workout_ua_upload(
request.user,w
)
except NoTokenError:
message = "Please connect to MapMyFitness first"
id = 0
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_tp):
try:
message,id = tpstuff.workout_tp_upload(
request.user,w
)
except NoTokenError:
message = "Please connect to TrainingPeaks first"
id = 0
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if race and race_can_submit(r,race):
records = IndoorVirtualRaceResult.objects.filter(
race=race,
userid=r.id
)
if records:
result,comments,errors,jobid = add_workout_indoorrace(
[w],race,r,recordid=records[0].id
)
if result:
messages.info(
request,
"We have submitted your workout to the race")
for c in comments:
messages.info(request,c)
for er in errors:
messages.error(request,er)
if landingpage != 'workout_upload_view':
url = reverse(landingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
})
else:
url = reverse(landingpage)
if is_ajax:
response = {'result':1,'url':url}
else:
response = HttpResponseRedirect(url)
else:
if not is_ajax:
response = render(request,
'document_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'optionsform': optionsform,
})
if is_ajax:
return JSONResponse(response)
else:
return response
else:
if not is_ajax:
if r.c2_auto_export and isprorower(r):
uploadoptions['upload_to_C2'] = True
if r.strava_auto_export and isprorower(r):
uploadoptions['upload_to_Strava'] = True
if r.sporttracks_auto_export and isprorower(r):
uploadoptions['upload_to_SportTracks'] = True
if r.runkeeper_auto_export and isprorower(r):
uploadoptions['upload_to_RunKeeper'] = True
if r.trainingpeaks_auto_export and isprorower(r):
uploadoptions['upload_to_TrainingPeaks'] = True
if r.mapmyfitness_auto_export and isprorower(r):
uploadoptions['upload_to_MapMyFitness'] = True
form = DocumentsForm(initial=docformoptions)
optionsform = UploadOptionsForm(initial=uploadoptions,
request=request)
return render(request, 'document_form.html',
{'form':form,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'optionsform': optionsform,
})
else:
return {'result':0}
# This is the main view for processing uploaded files
@user_passes_test(iscoachmember,login_url="/rowers/paidplans",redirect_field_name=None,
message="This functionality requires a Coach plan or higher")
def team_workout_upload_view(request,message="",
successmessage="",
uploadoptions={
'make_plot':False,
'plottype':'timeplot',
}):
if 'uploadoptions' in request.session:
uploadoptions = request.session['uploadoptions']
else:
request.session['uploadoptions'] = uploadoptions
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url': reverse('team_workout_upload_view'),
'name': 'Team Upload'
}
]
myteams = Team.objects.filter(manager=request.user)
make_plot = uploadoptions['make_plot']
plottype = uploadoptions['plottype']
r = getrower(request.user)
if request.method == 'POST':
form = DocumentsForm(request.POST,request.FILES)
optionsform = TeamUploadOptionsForm(request.POST)
rowerform = TeamInviteForm(request.POST)
rowerform.fields.pop('email')
rowers = Rower.objects.filter(
coachinggroups__in=[r.mycoachgroup]
).exclude(
rowerplan='freecoach'
).distinct()
if r.rowerplan == 'freecoach':
rowers = rowers.exclude(rowerplan='basic')
rowerform.fields['user'].queryset = User.objects.filter(rower__in=rowers).distinct()
if form.is_valid():
f = request.FILES['file']
res = handle_uploaded_file(f)
t = form.cleaned_data['title']
offline = form.cleaned_data['offline']
boattype = form.cleaned_data['boattype']
workouttype = form.cleaned_data['workouttype']
if rowerform.is_valid():
u = rowerform.cleaned_data['user']
if u and request.user.rower in teams.rower_get_coaches(u.rower):
r = getrower(u)
else:
message = 'Please select a rower'
messages.error(request,message)
messages.info(request,successmessage)
response = render(request,
'team_document_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'optionsform': optionsform,
'rowerform': rowerform,
})
return response
workouttype = form.cleaned_data['workouttype']
notes = form.cleaned_data['notes']
if optionsform.is_valid():
make_plot = optionsform.cleaned_data['make_plot']
plottype = optionsform.cleaned_data['plottype']
uploadoptions = {
'makeprivate':False,
'make_plot':make_plot,
'plottype':plottype,
'upload_to_C2':False,
}
request.session['uploadoptions'] = uploadoptions
f1 = res[0] # file name
f2 = res[1] # file name incl media directory
if not offline:
id,message,f2 = dataprep.new_workout_from_file(
r,f2,
workouttype=workouttype,
boattype=boattype,
makeprivate=False,
title = t,
notes=''
)
else:
job = myqueue(
queuehigh,
handle_zip_file,
r.user.email,
t,
f2,
emailbounced = r.emailbounced
)
messages.info(
request,
"The file was too large to process in real time. It will be processed in a background process. The user will receive an email when it is ready"
)
url = reverse('team_workout_upload_view')
response = HttpResponseRedirect(url)
return response
if not id:
messages.error(request,message)
url = reverse('team_workout_upload_view')
response = HttpResponseRedirect(url)
return response
elif id == -1:
message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.'
messages.info(request,message)
url = reverse('team_workout_upload_view')
response = HttpResponseRedirect(url)
return response
else:
successmessage = "The workout was added to the user's account"
messages.info(request,successmessage)
url = reverse('team_workout_upload_view')
response = HttpResponseRedirect(url)
w = Workout.objects.get(id=id)
r = getrower(request.user)
if (make_plot):
id,jobid = uploads.make_plot(r,w,f1,f2,plottype,t)
else:
response = render(request,
'team_document_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'active': 'nav-workouts',
'breadcrumbs':breadcrumbs,
'optionsform': optionsform,
'rowerform': rowerform,
})
return response
else:
form = DocumentsForm()
optionsform = TeamUploadOptionsForm(initial=uploadoptions)
rowerform = TeamInviteForm()
rowerform.fields.pop('email')
rowers = Rower.objects.filter(
coachinggroups__in=[r.mycoachgroup]
).exclude(
rowerplan='freecoach'
).distinct()
if r.rowerplan == 'freecoach':
rowers = rowers.exclude(rowerplan='basic')
rowerform.fields['user'].queryset = User.objects.filter(rower__in=rowers).distinct()
return render(request, 'team_document_form.html',
{'form':form,
# 'teams':get_my_teams(request.user),
'optionsform': optionsform,
'active': 'nav-workouts',
'breadcrumbs':breadcrumbs,
# 'rower':r,
'rowerform':rowerform,
})
# A page with all the recent graphs (searchable on workout name)
@login_required()
def graphs_view(request):
request.session['referer'] = reverse('graphs_view')
try:
r = getrower(request.user)
workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
searchform = SearchForm(initial={'q':query})
else:
searchform = SearchForm()
g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime")
paginator = Paginator(g,8)
page = request.GET.get('page')
try:
g = paginator.page(page)
except PageNotAnInteger:
g = paginator.page(1)
except EmptyPage:
g = paginator.page(paginator.num_pages)
return render(request, 'list_graphs.html',
{'graphs': g,
'searchform':searchform,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
})
except Rower.DoesNotExist:
raise Http404("User has no rower instance")
# Show the chart (png image)
def graph_show_view(request,id):
try:
g = GraphImage.objects.get(id=id)
try:
width,height = Image.open(g.filename).size
g.width = width
g.height = height
g.save()
except:
pass
w = Workout.objects.get(id=g.workout.id)
r = Rower.objects.get(id=w.user.id)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,encoder.encode_hex(w.id)),
'name': w.name
},
{
'url':reverse('graph_show_view',kwargs={'id':id}),
'name': 'Chart'
}
]
return render(request,'show_graph.html',
{'graph':g,
'teams':get_my_teams(request.user),
'workout':w,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'rower':r,})
except GraphImage.DoesNotExist:
raise Http404("This graph doesn't exist")
except Workout.DoesNotExist:
raise Http404("This workout doesn't exist")
# Restore original stroke data and summary
@login_required()
def workout_summary_restore_view(request,id,message="",successmessage=""):
row = get_workout_permitted(request.user,id)
s = ""
# still here - this is a workout we may edit
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1,
r.pw_at,
r.pw_tr,r.pw_an])/r.ftp
ftp = float(r.ftp)
if row.workouttype in mytypes.otwtypes:
ftp = ftp*(100.-r.otwslack)/100.
rr = rrower(hrmax=r.max,hrut2=r.ut2,
hrut1=r.ut1,hrat=r.at,
hrtr=r.tr,hran=r.an,ftp=ftp,
powerperc=powerperc,powerzones=r.powerzones)
rowdata = rdata(f1,rower=rr)
if rowdata == 0:
raise Http404("Error: CSV Data File Not Found")
rowdata.restoreintervaldata()
rowdata.write_csv(f1,gzip=True)
dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df)
intervalstats = rowdata.allstats()
row.summary = intervalstats
row.save()
# create interactive plot
try:
res = interactive_chart(encoder.decode_hex(id),promember=1)
script = res[0]
div = res[1]
except ValueError:
pass
messages.info(request,'Original Interval Data Restored')
url = reverse('workout_summary_edit_view',
kwargs={
'id':encoder.encode_hex(row.id),
}
)
return HttpResponseRedirect(url)
# Split a workout
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_split_view(request,id=0):
row = get_workout_permitted(request.user,id)
r = row.user
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse('workout_split_view',
kwargs={'id':id}),
'name': 'Chart'
}
]
if request.method == 'POST':
form = WorkoutSplitForm(request.POST)
if form.is_valid():
splittime = form.cleaned_data['splittime']
splitsecond = splittime.hour*3600
splitsecond += splittime.minute*60
splitsecond += splittime.second
splitsecond += splittime.microsecond/1.e6
splitmode = form.cleaned_data['splitmode']
try:
ids,mesgs = dataprep.split_workout(
r,row,splitsecond,splitmode
)
except IndexError:
messages.error(request,"Something went wrong in Split")
for message in mesgs:
messages.info(request,message)
if request.user == r:
url = reverse('workouts_view')
else:
mgrids = [team.id for team in Team.objects.filter(manager=request.user)]
rwrids = [team.id for team in r.team.all()]
teamids = list(set(mgrids) & set(rwrids))
if len(teamids) > 0:
teamid = teamids[0]
url = reverse('workouts_view',
kwargs={
'teamid':int(teamid),
}
)
else:
url = reverse('workouts_view')
rowname = row.name
try:
if isinstance(rowname,unicode):
rowname = rowname.encode('utf8')
elif isinstance(rowname, str):
rowname = rowname.decode('utf8')
except:
pass
qdict = {'q':rowname}
url+='?'+urllib.parse.urlencode(qdict)
return HttpResponseRedirect(url)
form = WorkoutSplitForm()
# create interactive plot
try:
res = interactive_chart(encoder.decode_hex(id),promember=1)
script = res[0]
div = res[1]
except ValueError:
pass
return render(request, 'splitworkout.html',
{'form':form,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'workout':row,
'thediv':script,
'thescript':div,
})
# Fuse two workouts
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def workout_fusion_view(request,id1=0,id2=1):
try:
id1 = encoder.decode_hex(id1)
id2 = encoder.decode_hex(id2)
except:
pass
r = getrower(request.user)
try:
w1 = Workout.objects.get(id=id1)
w2 = Workout.objects.get(id=id2)
r = w1.user
if (checkworkoutuser(request.user,w1)==False) or \
(checkworkoutuser(request.user,w2)==False):
raise PermissionDenied("You are not allowed to use these workouts")
except Workout.DoesNotExist:
raise Http404("One of the workouts doesn't exist")
if request.method == 'POST':
form = FusionMetricChoiceForm(request.POST,instance=w2)
if form.is_valid():
cd = form.cleaned_data
columns = cd['columns']
timeoffset = cd['offset']
posneg = cd['posneg']
if posneg == 'neg':
timeoffset = -timeoffset
# Create DataFrame
df,forceunit = dataprep.datafusion(id1,id2,columns,timeoffset)
idnew,message = dataprep.new_workout_from_df(r,df,
title='Fused data',
parent=w1,
forceunit=forceunit)
if message != None:
messages.error(request,message)
else:
successmessage = 'Data fused'
messages.info(request,message)
url = reverse('workout_edit_view',
kwargs={
'id':encoder.encode_hex(idnew),
})
return HttpResponseRedirect(url)
else:
form = FusionMetricChoiceForm(instance=w2)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,encoder.encode_hex(w1.id)),
'name': encoder.encode_hex(w1.id)
},
{
'url':reverse('workout_fusion_list',
kwargs={'id':encoder.encode_hex(id1)}),
'name': 'Sensor Fusion'
},
{
'url':reverse('workout_fusion_view',
kwargs={
'id1':encoder.encode_hex(id1),
'id2':encoder.encode_hex(id2)
}),
'name': encoder.encode_hex(w2.id)
}
]
return render(request, 'fusion.html',
{'form':form,
'teams':get_my_teams(request.user),
'workout':w1,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'workout1':w1,
'workout2':w2,
})
# Edit the splits/summary
@login_required()
def workout_summary_edit_view(request,id,message="",successmessage=""
):
row = get_workout_permitted(request.user,id)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,encoder.encode_hex(row.id)),
'name': row.name
},
{
'url':reverse('workout_summary_edit_view',kwargs={'id':id}),
'name': 'Edit Intervals'
}
]
s = ""
# still here - this is a workout we may edit
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1,
r.pw_at,
r.pw_tr,r.pw_an])/r.ftp
ftp = float(r.ftp)
if row.workouttype in mytypes.otwtypes:
ftp = ftp*(100.-r.otwslack)/100.
rr = rrower(hrmax=r.max,hrut2=r.ut2,
hrut1=r.ut1,hrat=r.at,
hrtr=r.tr,hran=r.an,ftp=ftp,
powerperc=powerperc,powerzones=r.powerzones)
rowdata = rdata(f1,rower=rr)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
intervalstats = rowdata.allstats()
try:
itime,idist,itype = rowdata.intervalstats_values()
except TypeError:
return HttpResponse("Error: CSV Data File Not Found")
nrintervals = len(idist)
savebutton = 'nosavebutton'
formvalues = {}
form = SummaryStringForm()
tss,normp = dataprep.workout_rscore(row)
normv,normw = dataprep.workout_normv(row,pp=8.0)
work = int(normw)
power = int(normp)
try:
pace_secs = int(500./normv)
except (OverflowError, ZeroDivisionError):
pace_secs = 140.
try:
avpace = datetime.timedelta(seconds=int(500./normv))
except (OverflowError, ZeroDivisionError):
avpace = datetime.timedelta(seconds=130)
data = {
'power': int(normp),
'pace': avpace,
'selector': 'power',
'work': int(normw)
}
powerorpace = 'power'
if normp == 0:
data['selector'] = 'pace'
powerorpace = 'pace'
powerupdateform = PowerIntervalUpdateForm(initial=data)
# We have submitted the mini language interpreter
if request.method == 'POST' and "intervalstring" in request.POST:
form = SummaryStringForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
s = cd["intervalstring"]
try:
rowdata.updateinterval_string(s)
except:
messages.error(request,'Parsing error')
intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist)
savebutton = 'savestringform'
powerupdateform = PowerIntervalUpdateForm(initial=data)
# we are saving the results obtained from the split by power/pace interpreter
elif request.method == 'POST' and "savepowerpaceform" in request.POST:
powerorpace = request.POST['powerorpace']
value_pace = request.POST['value_pace']
value_power = request.POST['value_power']
value_work = request.POST['value_work']
if powerorpace == 'power':
try:
power = int(value_power)
except ValueError:
int(normp)
elif powerorpace == 'pace':
try:
pace_secs = float(value_pace)
except ValueError:
try:
pace_secs = float(value_pace.replace(',','.'))
except ValueError:
pace_secs = int(500./normv)
elif powerorpace == 'work':
try:
work = int(value_work)
except ValueError:
work = int(normw)
if powerorpace == 'power' and power is not None:
try:
rowdata.updateinterval_metric(
' Power (watts)',power,mode='larger',
debug=False,smoothwindow=15.)
except:
messages.error(request,'Error updating power')
elif powerorpace == 'pace':
try:
velo = 500./pace_secs
rowdata.updateinterval_metric(
' AverageBoatSpeed (m/s)',velo,mode='larger',
debug=False,smoothwindow=15.)
except:
messages.error(request,'Error updating pace')
elif powerorpace == 'work':
try:
rowdata.updateinterval_metric(
'driveenergy',work,mode='larger',
debug=False,smoothwindow=15.)
except:
messages.error(request,'Error updating Work per Stroke')
intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist)
row.summary = intervalstats
row.save()
rowdata.write_csv(f1,gzip=True)
dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df)
messages.info(request,"Updated interval data saved")
data = {
'power': power,
'pace': datetime.timedelta(seconds=int(pace_secs)),
'work': work,
'selector': powerorpace
}
form = SummaryStringForm()
powerupdateform = PowerIntervalUpdateForm(initial=data)
savebutton = 'savepowerpaceform'
formvalues = {
'powerorpace': powerorpace,
'value_power': power,
'value_pace': pace_secs,
'value_work':work,
}
# we are saving the results obtained from the mini language interpreter
elif request.method == 'POST' and "savestringform" in request.POST:
s = request.POST["savestringform"]
try:
rowdata.updateinterval_string(s)
except (ParseException,err):
messages.error(request,'Parsing error in column '+str(err.col))
intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist)
row.summary = intervalstats
#intervalstats = rowdata.allstats()
if s:
try:
row.notes = u'{n} \n {s}'.format(
n = row.notes,
s = s
)
except TypeError:
pass
row.save()
rowdata.write_csv(f1,gzip=True)
dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df)
messages.info(request,"Updated interval data saved")
data = {'intervalstring':s}
form = SummaryStringForm(initial=data)
powerupdateform = PowerIntervalUpdateForm(initial={
'power': int(normp),
'pace': avpace,
'selector': 'power',
'work': int(normw)
})
savebutton = 'savestringform'
# we are saving the results obtained from the power update form
elif request.method == 'POST' and 'selector' in request.POST:
powerupdateform = PowerIntervalUpdateForm(request.POST)
if powerupdateform.is_valid():
cd = powerupdateform.cleaned_data
powerorpace = cd['selector']
power = cd['power']
pace = cd['pace']
work = cd['work']
try:
pace_secs = pace.seconds+pace.microseconds/1.0e6
except AttributeError:
pace_secs = 120.
if powerorpace == 'power' and power is not None:
try:
rowdata.updateinterval_metric(' Power (watts)',power,mode='larger',
debug=False,smoothwindow=15)
except:
messages.error(request,'Error updating power')
elif powerorpace == 'pace':
try:
velo = 500./pace_secs
rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',velo,mode='larger',
debug=False,smoothwindow=15)
except:
messages.error(request,'Error updating pace')
elif powerorpace == 'work':
try:
rowdata.updateinterval_metric(
'driveenergy',work,mode='larger',
debug=False,smoothwindow=15.)
except:
messages.error(request,'Error updating Work per Stroke')
intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist)
savebutton = 'savepowerpaceform'
formvalues = {
'powerorpace': powerorpace,
'value_power': power,
'value_pace': pace_secs,
'value_work': work,
}
powerupdateform = PowerIntervalUpdateForm(initial=cd)
form = SummaryStringForm()
form = SummaryStringForm()
# we are saving the results obtained from the detailed form
elif request.method == 'POST' and "savedetailform" in request.POST:
savebutton = 'savedetailform'
form = SummaryStringForm()
nrintervals = int(request.POST['nrintervals'])
detailform = IntervalUpdateForm(request.POST,aantal=nrintervals)
itime = []
idist = []
itype = []
ivalues = []
iunits = []
itypes = []
iresults = []
for i in range(nrintervals):
try:
t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S.%f")
except ValueError:
t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S")
timesecs = 3600*t.hour+60*t.minute+t.second+t.microsecond/1.e6
itime += [timesecs]
idist += [int(request.POST['intervald_%s' % i])]
itype += [int(request.POST['type_%s' % i])]
if itype[i] == 3: # rest
itypes += ['rest']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
if itype[i] == 5 or itype[i] == 2: # distance based work
itypes += ['work']
ivalues += [idist[i]]
iresults += [timesecs]
iunits += ['meters']
if itype[i] == 4 or itype[i] == 1: # time based work
itypes += ['work']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
rowdata.updateintervaldata(ivalues,iunits,itypes,iresults=iresults)
intervalstats = rowdata.allstats()
row.summary = intervalstats
try:
row.notes += "\n"+s
except TypeError:
pass
row.save()
rowdata.write_csv(f1,gzip=True)
dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df)
messages.info(request,"Updated interval data saved")
form = SummaryStringForm()
powerupdateform = PowerIntervalUpdateForm(initial={
'power': int(normp),
'pace': avpace,
'selector': 'power',
'work': int(normw)
})
# we are processing the details form
elif request.method == 'POST' and "nrintervals" in request.POST:
savebutton = 'savedetailform'
nrintervals = int(request.POST['nrintervals'])
detailform = IntervalUpdateForm(request.POST,aantal=nrintervals)
if detailform.is_valid():
cd = detailform.cleaned_data
itime = []
idist = []
itype = []
ivalues = []
iunits = []
itypes = []
iresults = []
for i in range(nrintervals):
t = cd['intervalt_%s' % i]
timesecs = t.total_seconds()
itime += [timesecs]
idist += [cd['intervald_%s' % i]]
itype += [cd['type_%s' % i]]
if itype[i] == '3': # rest
itypes += ['rest']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
if itype[i] == '5' or itype[i] == '2': # distance based work
itypes += ['work']
ivalues += [idist[i]]
iresults += [timesecs]
iunits += ['meters']
if itype[i] == '4' or itype[i] == '1': # time based work
itypes += ['work']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
rowdata.updateintervaldata(ivalues,iunits,
itypes,iresults=iresults)
intervalstats = rowdata.allstats()
form = SummaryStringForm()
powerupdateform = PowerIntervalUpdateForm()
initial = {}
for i in range(nrintervals):
try:
initial['intervald_%s' % i] = idist[i]
initial['intervalt_%s' % i] = get_time(itime[i])
initial['type_%s' % i] = itype[i]
except IndexError:
pass
detailform = IntervalUpdateForm(aantal=nrintervals,initial=initial)
# create interactive plot
try:
intervaldata = {
'itime':itime,
'idist':idist,
'itype':itype,
'selector': powerorpace,
'normp': normp,
'normv': normv,
}
res = interactive_chart(encoder.decode_hex(id),promember=1,intervaldata=intervaldata)
script = res[0]
div = res[1]
except ValueError:
script = ''
div = ''
# render page
return render(request, 'summary_edit.html',
{'form':form,
'detailform':detailform,
'powerupdateform':powerupdateform,
'workout':row,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
'intervalstats':intervalstats,
'nrintervals':nrintervals,
'interactiveplot':script,
'the_div':div,
'intervalstring':s,
'savebutton':savebutton,
'formvalues':formvalues,
})
class GraphDelete(DeleteView):
login_required = True
model = GraphImage
template_name = 'graphimage_delete_confirm.html'
# extra parameters
def get_context_data(self, **kwargs):
context = super(GraphDelete, self).get_context_data(**kwargs)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(
self.request,
encoder.encode_hex(self.object.workout.id)),
'name': self.object.workout.name
},
{
'url':reverse('graph_show_view',kwargs={'id':self.object.pk}),
'name': 'Chart'
},
{ 'url':reverse('graph_delete',kwargs={'pk':str(self.object.pk)}),
'name': 'Delete'
}
]
context['active'] = 'nav-workouts'
context['rower'] = getrower(self.request.user)
context['breadcrumbs'] = breadcrumbs
return context
def get_success_url(self):
w = self.object.workout
try:
w = Workout.objects.get(id=w.id)
except Workout.DoesNotExist:
return reverse('workouts_view')
return reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)})
def get_object(self, *args, **kwargs):
obj = super(GraphDelete, self).get_object(*args, **kwargs)
if not checkaccessuser(self.request.user,obj.workout.user):
raise PermissionDenied('You are not allowed to delete this chart')
return obj
def workout_code_delete_view(request,id=0):
pk = encoder.decode_hex(id)
url = reverse('workout_delete',kwargs={'pk':pk})
return HttpResponseRedirect(url)
class WorkoutDelete(DeleteView):
login_required = True
model = Workout
template_name = 'workout_delete_confirm.html'
# extra parameters
def get_context_data(self, **kwargs):
context = super(WorkoutDelete, self).get_context_data(**kwargs)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':get_workout_default_page(self.request,encoder.encode_hex(self.object.id)),
'name': encoder.encode_hex(self.object.id)
},
{ 'url':reverse('workout_delete',kwargs={'pk':str(self.object.pk)}),
'name': 'Delete'
}
]
mayedit=0
promember=0
if not self.request.user.is_anonymous:
r = getrower(self.request.user)
result = self.request.user.is_authenticated and ispromember(self.request.user)
if result:
promember=1
if self.request.user == self.object.user.user:
mayedit=1
context['active'] = 'nav-workouts'
context['rower'] = getrower(self.request.user)
context['breadcrumbs'] = breadcrumbs
context['workout'] = self.object
context['mayedit'] = mayedit
context['promember'] = promember
return context
def get_success_url(self):
return reverse('workouts_view')
def get_object(self, *args, **kwargs):
obj = super(WorkoutDelete, self).get_object(*args, **kwargs)
if not checkaccessuser(self.request.user,obj.user):
raise PermissionDenied('You are not allowed to delete this workout')
return obj