Private
Public Access
1
0
This commit is contained in:
Sander Roosendaal
2020-02-24 15:36:21 +01:00
9 changed files with 157 additions and 44 deletions

View File

@@ -174,7 +174,7 @@ ratelim==0.1.6
redis==3.2.1 redis==3.2.1
requests==2.21.0 requests==2.21.0
requests-oauthlib==1.2.0 requests-oauthlib==1.2.0
rowingdata==2.6.7 rowingdata==2.7.2
rowingphysics==0.5.0 rowingphysics==0.5.0
rq==0.13.0 rq==0.13.0
rules==2.1 rules==2.1

View File

@@ -1390,7 +1390,7 @@ def parsenonpainsled(fileformat,f2,summary):
try: try:
row = parsers[fileformat](f2) row = parsers[fileformat](f2)
hasrecognized = True hasrecognized = True
except (KeyError,IndexError): except (KeyError,IndexError,ValueError):
hasrecognized = False hasrecognized = False
return None, hasrecognized, '', 'unknown' return None, hasrecognized, '', 'unknown'
@@ -1589,6 +1589,8 @@ def new_workout_from_file(r, f2,
impeller=impeller, impeller=impeller,
) )
job = myqueue(queuehigh,handle_calctrimp,id,f2,r.ftp,r.sex,r.hrftp,r.max,r.rest)
return (id, message, f2) return (id, message, f2)

View File

@@ -731,15 +731,19 @@ class PowerIntervalUpdateForm(forms.Form):
('power','Power'), ('power','Power'),
('pace','Pace'), ('pace','Pace'),
('work','Work per Stroke'), ('work','Work per Stroke'),
('spm','Stroke Rate')
) )
pace = forms.DurationField(required=False,label='Pace (/500m)') pace = forms.DurationField(required=False,label='Pace (/500m)')
power = forms.IntegerField(required=False,label='Power (W)') power = forms.IntegerField(required=False,label='Power (W)')
work = forms.IntegerField(required=False,label='Work per Stroke (J)') work = forms.IntegerField(required=False,label='Work per Stroke (J)')
spm = forms.IntegerField(required=False,label='Stroke Rate')
selector = forms.ChoiceField(choices=selectorchoices, selector = forms.ChoiceField(choices=selectorchoices,
required=True, required=True,
initial='power', initial='power',
label='Use') label='Use')
activeminutesmin = forms.IntegerField(required=False,initial=0,widget=forms.HiddenInput())
activeminutesmax = forms.IntegerField(required=False,initial=0,widget=forms.HiddenInput())
# Form used to update interval stats # Form used to update interval stats
class IntervalUpdateForm(forms.Form): class IntervalUpdateForm(forms.Form):

View File

@@ -259,9 +259,9 @@ def is_rower_team_member(user,rower):
for team in teams: for team in teams:
if team.private == 'open': if team.private == 'open':
if team in rower.team.all(): if team in user.rower.team.all():
return True return True
if team.manager == rower.user: if team.manager == user:
return True return True
return False return False

View File

@@ -7,6 +7,27 @@
{% block scripts %} {% block scripts %}
{% include "monitorjobs.html" %} {% include "monitorjobs.html" %}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$( function() {
console.log({{ activeminutesmin }}, {{ activeminutesmax}}, 'active range');
$( "#slider-range" ).slider({
range: true,
min: 0,
max: {{ maxminutes }},
values: [ {{ activeminutesmin }}, {{ activeminutesmax }} ],
slide: function( event, ui ) {
$( "#amount" ).val(ui.values[ 0 ] + " min - " + ui.values[ 1 ] + " min " );
$("#id_activeminutesmin").val(ui.values[0]);
$("#id_activeminutesmax").val(ui.values[1]);
}
});
$( "#amount" ).val($( "#slider-range" ).slider( "values", 0 ) +
" min - " + $( "#slider-range" ).slider( "values", 1 ) + " min ");
} );
</script>
{% endblock %} {% endblock %}
{% block main %} {% block main %}
@@ -47,22 +68,28 @@
{{ form.as_table }} {{ form.as_table }}
</table> </table>
{% csrf_token %} {% csrf_token %}
<input class="button green" type="submit" value="Update"> <input class="button" type="submit" value="Update">
</form> </form>
<h1>Intervals by Power/Pace</h1> <h1>Intervals by Power/Pace</h1>
<p>With this form, you can specify a power or pace level. Everything faster/harder than the <p>With this form, you can specify a power or pace level. Everything faster/harder than the
specified pace/power will become a work interval. Everything slower will become a rest specified pace/power will become a work interval. Everything slower will become a rest
interval. interval. Use the slider to limit the active range. Everything outside the active range will
become rest (warming up and cooling down).
</p> </p>
<form ecntype="multipart/form-data" method="post"> <form ecntype="multipart/form-data" method="post">
<div id="slider-range"></div>
<p>
<label for="amount">Active Range:</label>
<input type="text" id="amount" readonly style="border:0; color:#1c75bc; font-weight:bold;">
</p>
<table> <table>
{{ powerupdateform.as_table }} {{ powerupdateform.as_table }}
</table> </table>
{% csrf_token %} {% csrf_token %}
<input class="button green" type="submit" value="Submit"> <input class="button" type="submit" value="Submit">
</form> </form>
</li> </li>
<li class="grid_2"> <li class="grid_2">
@@ -131,7 +158,7 @@
</table> </table>
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="nrintervals" value={{ nrintervals }}> <input type="hidden" name="nrintervals" value={{ nrintervals }}>
<input class="button green" type="submit" value="Update"> <input class="button" type="submit" value="Update">
</form> </form>
</li> </li>
<li class="grid_4"> <li class="grid_4">

View File

@@ -564,6 +564,9 @@ class WorkoutViewTest(TestCase):
'value_pace':'2:23', 'value_pace':'2:23',
'value_power':'200', 'value_power':'200',
'value_work':'400', 'value_work':'400',
'value_spm':'20',
'activeminutesmin':'0',
'activeminutesmax':'60',
'savepowerpaceform':True, 'savepowerpaceform':True,
} }
@@ -582,6 +585,8 @@ class WorkoutViewTest(TestCase):
'power': 200, 'power': 200,
'pace': '2:30', 'pace': '2:30',
'work': 400, 'work': 400,
'activeminutesmin':'0',
'activeminutesmax': '60',
} }
form = PowerIntervalUpdateForm(form_data) form = PowerIntervalUpdateForm(form_data)

View File

@@ -356,7 +356,8 @@ urlpatterns = [
name='workout_video_view_mini'), name='workout_video_view_mini'),
re_path(r'^video/(?P<id>\w.+)/$',views.workout_video_view, re_path(r'^video/(?P<id>\w.+)/$',views.workout_video_view,
name='workout_video_view'), name='workout_video_view'),
re_path(r'^videos/',views.list_videos,name='list_videos'), re_path(r'^videos/$',views.list_videos,name='list_videos'),
re_path(r'^videos/user/(?P<userid>\d+)/$',views.list_videos,name='list_videos'),
re_path(r'^add-video/user/(?P<userid>\d+)/$',views.video_selectworkout,name='video_selectworkout'), re_path(r'^add-video/user/(?P<userid>\d+)/$',views.video_selectworkout,name='video_selectworkout'),
re_path(r'^add-video/',views.video_selectworkout,name='video_selectworkout'), re_path(r'^add-video/',views.video_selectworkout,name='video_selectworkout'),
# re_path(r'^workout/(?P<id>\d+)/$',views.workout_view,name='workout_view'), # re_path(r'^workout/(?P<id>\d+)/$',views.workout_view,name='workout_view'),

View File

@@ -45,7 +45,8 @@ from rowers.rower_rules import (
can_view_plan,can_change_plan,can_delete_plan, can_view_plan,can_change_plan,can_delete_plan,
can_view_cycle,can_change_cycle,can_delete_cycle, can_view_cycle,can_change_cycle,can_delete_cycle,
can_add_workout_member,can_plan_user,is_paid_coach, can_add_workout_member,can_plan_user,is_paid_coach,
can_start_trial, can_start_plantrial,can_plan,is_workout_team can_start_trial, can_start_plantrial,can_plan,is_workout_team,
is_promember,
) )
from django.shortcuts import render from django.shortcuts import render
@@ -373,8 +374,8 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False):
userid = int(userid) userid = int(userid)
rowerid = int(rowerid) rowerid = int(rowerid)
if userid == 0: #if userid == 0:
userid = request.user.id # userid = request.user.id
if notpermanent == False: if notpermanent == False:
if rowerid == 0 and 'rowerid' in request.session: if rowerid == 0 and 'rowerid' in request.session:

View File

@@ -1762,11 +1762,6 @@ def workouts_view(request,message='',successmessage='',
r = getrequestrower(request,rowerid=rowerid,userid=userid) r = getrequestrower(request,rowerid=rowerid,userid=userid)
# check if access is allowed # check if access is allowed
if not is_rower_team_member(request.user,r):
request.session['rowerid'] = request.user.rower.id
raise PermissionDenied("Access denied")
startdate = datetime.datetime.combine(startdate,datetime.time()) startdate = datetime.datetime.combine(startdate,datetime.time())
@@ -4497,7 +4492,7 @@ def workout_upload_api(request):
if id == 0: if id == 0:
if message is not None: if message is not None:
message = {'status':'false','message':'unable to process file'+message} message = {'status':'false','message':'unable to process file: '+message}
else: else:
message = {'status': 'false', 'message': 'unable to process file'} message = {'status': 'false', 'message': 'unable to process file'}
return JSONResponse(status=400,data=message) return JSONResponse(status=400,data=message)
@@ -5050,9 +5045,8 @@ def team_workout_upload_view(request,message="",
workouttype = form.cleaned_data['workouttype'] workouttype = form.cleaned_data['workouttype']
if rowerform.is_valid(): if rowerform.is_valid():
u = rowerform.cleaned_data['user'] u = rowerform.cleaned_data['user']
if can_add_workout_member(request.user,u.rower): r = getrower(u)
r = getrower(u) if not can_add_workout_member(request.user,r):
else:
message = 'Please select a rower' message = 'Please select a rower'
messages.error(request,message) messages.error(request,message)
messages.info(request,successmessage) messages.info(request,successmessage)
@@ -5192,8 +5186,8 @@ def team_workout_upload_view(request,message="",
# A page with all the recent graphs (searchable on workout name) # A page with all the recent graphs (searchable on workout name)
@login_required() @login_required()
def list_videos(request): def list_videos(request,userid=0):
r = getrequestrower(request) r = getrequestrower(request,userid=userid)
workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime") workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
query = request.GET.get('q') query = request.GET.get('q')
if query: if query:
@@ -5227,6 +5221,7 @@ def list_videos(request):
{'analyses': g, {'analyses': g,
'searchform':searchform, 'searchform':searchform,
'active':'nav-analysis', 'active':'nav-analysis',
'rower':r,
'teams':get_my_teams(request.user), 'teams':get_my_teams(request.user),
}) })
@@ -5605,6 +5600,9 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
return HttpResponse("Error: CSV Data File Not Found") return HttpResponse("Error: CSV Data File Not Found")
nrintervals = len(idist) nrintervals = len(idist)
activeminutesmax = int(rowdata.duration/60.)
activeminutesmin = 0
maxminutes = activeminutesmax
savebutton = 'nosavebutton' savebutton = 'nosavebutton'
formvalues = {} formvalues = {}
@@ -5614,6 +5612,10 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
normp = row.normp normp = row.normp
normv = row.normv normv = row.normv
normw = row.normw normw = row.normw
try:
normspm = rowdata.df[' Cadence (stokes/min)'].mean()
except KeyError:
normspm = 0
if tss == -1: if tss == -1:
tss,normp = dataprep.workout_rscore(row) tss,normp = dataprep.workout_rscore(row)
@@ -5635,7 +5637,10 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
'power': int(normp), 'power': int(normp),
'pace': avpace, 'pace': avpace,
'selector': 'power', 'selector': 'power',
'work': int(normw) 'work': int(normw),
'spm': int(normspm),
'activeminutesmin': 0,
'activeminutesmax': activeminutesmax,
} }
powerorpace = 'power' powerorpace = 'power'
@@ -5670,6 +5675,20 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
value_pace = request.POST['value_pace'] value_pace = request.POST['value_pace']
value_power = request.POST['value_power'] value_power = request.POST['value_power']
value_work = request.POST['value_work'] value_work = request.POST['value_work']
value_spm = request.POST['value_spm']
activeminutesmin = request.POST['activeminutesmin']
activeminutesmax = request.POST['activeminutesmax']
try:
activesecondsmin = 60.*float(activeminutesmin)
activesecondsmax = 60.*float(activeminutesmax)
except ValueError:
activesecondsmin = 0
activeminutesmin = 0
activeminutesmax = maxminutes
activesecondsmax = rowdata.duration
if abs(rowdata.duration-activesecondsmax) < 60.:
activesecondsmax = rowdata.duration
if powerorpace == 'power': if powerorpace == 'power':
try: try:
power = int(value_power) power = int(value_power)
@@ -5688,12 +5707,19 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
work = int(value_work) work = int(value_work)
except ValueError: except ValueError:
work = int(normw) work = int(normw)
elif powerorpace == 'spm':
try:
spm = int(value_spm)
except ValueError:
spm = int(normspm)
if powerorpace == 'power' and power is not None: if powerorpace == 'power' and power is not None:
try: try:
rowdata.updateinterval_metric( rowdata.updateinterval_metric(
' Power (watts)',power,mode='larger', ' Power (watts)',power,mode='larger',
debug=False,smoothwindow=15.) debug=False,smoothwindow=15.,
activewindow=[activesecondsmin,activesecondsmax],
)
except: except:
messages.error(request,'Error updating power') messages.error(request,'Error updating power')
elif powerorpace == 'pace': elif powerorpace == 'pace':
@@ -5701,16 +5727,29 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
velo = 500./pace_secs velo = 500./pace_secs
rowdata.updateinterval_metric( rowdata.updateinterval_metric(
' AverageBoatSpeed (m/s)',velo,mode='larger', ' AverageBoatSpeed (m/s)',velo,mode='larger',
debug=False,smoothwindow=15.) debug=False,smoothwindow=15.,
activewindow=[activesecondsmin,activesecondsmax],
)
except: except:
messages.error(request,'Error updating pace') messages.error(request,'Error updating pace')
elif powerorpace == 'work': elif powerorpace == 'work':
try: try:
rowdata.updateinterval_metric( rowdata.updateinterval_metric(
'driveenergy',work,mode='larger', 'driveenergy',work,mode='larger',
debug=False,smoothwindow=15.) debug=False,smoothwindow=15.,
activewindow=[activesecondsmin,activesecondsmax],
)
except: except:
messages.error(request,'Error updating Work per Stroke') messages.error(request,'Error updating Work per Stroke')
elif powerorpace == 'spm':
try:
print('aap')
rowdata.updateinterval_metric(
' Cadence (stokes/min)',spm,mode='larger',
debug=False,smoothwindow=2.,
activewindow=[activesecondsmin,activesecondsmax],)
except:
messages.error(request,'Error updating SPM')
intervalstats = rowdata.allstats() intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values() itime,idist,itype = rowdata.intervalstats_values()
@@ -5728,7 +5767,10 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
'power': power, 'power': power,
'pace': datetime.timedelta(seconds=int(pace_secs)), 'pace': datetime.timedelta(seconds=int(pace_secs)),
'work': work, 'work': work,
'selector': powerorpace 'selector': powerorpace,
'spm': int(normspm),
'activeminutesmin': activeminutesmin,
'activeminutesmax': activeminutesmax,
} }
form = SummaryStringForm() form = SummaryStringForm()
powerupdateform = PowerIntervalUpdateForm(initial=data) powerupdateform = PowerIntervalUpdateForm(initial=data)
@@ -5773,7 +5815,10 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
'power': int(normp), 'power': int(normp),
'pace': avpace, 'pace': avpace,
'selector': 'power', 'selector': 'power',
'work': int(normw) 'work': int(normw),
'spm': int(normspm),
'activeminutesmin': 0,
'activeminutesmax': activeminutesmax,
}) })
savebutton = 'savestringform' savebutton = 'savestringform'
@@ -5786,31 +5831,50 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
power = cd['power'] power = cd['power']
pace = cd['pace'] pace = cd['pace']
work = cd['work'] work = cd['work']
spm = cd['spm']
activeminutesmin = cd['activeminutesmin']
activeminutesmax = cd['activeminutesmax']
activesecondsmin = 60.*activeminutesmin
activesecondsmax = 60.*activeminutesmax
if abs(rowdata.duration-activesecondsmax) < 60.:
activesecondsmax = rowdata.duration
try: try:
pace_secs = pace.seconds+pace.microseconds/1.0e6 pace_secs = pace.seconds+pace.microseconds/1.0e6
except AttributeError: except AttributeError:
pace_secs = 120. pace_secs = 120.
if powerorpace == 'power' and power is not None: if powerorpace == 'power' and power is not None:
try: rowdata.updateinterval_metric(' Power (watts)',power,mode='larger',
rowdata.updateinterval_metric(' Power (watts)',power,mode='larger', debug=False,smoothwindow=15,
debug=False,smoothwindow=15) activewindow=[activesecondsmin,activesecondsmax],
except: )
messages.error(request,'Error updating power')
elif powerorpace == 'pace': elif powerorpace == 'pace':
try: try:
velo = 500./pace_secs velo = 500./pace_secs
rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',velo,mode='larger', rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',velo,mode='larger',
debug=False,smoothwindow=15) debug=False,smoothwindow=15,
activewindow=[activesecondsmin,activesecondsmax],
)
except: except:
messages.error(request,'Error updating pace') messages.error(request,'Error updating pace')
elif powerorpace == 'work': elif powerorpace == 'work':
try: try:
rowdata.updateinterval_metric( rowdata.updateinterval_metric(
'driveenergy',work,mode='larger', 'driveenergy',work,mode='larger',
debug=False,smoothwindow=15.) debug=False,smoothwindow=15.,
activewindow=[activesecondsmin,activesecondsmax],)
except: except:
messages.error(request,'Error updating Work per Stroke') messages.error(request,'Error updating Work per Stroke')
elif powerorpace == 'spm':
try:
rowdata.updateinterval_metric(' Cadence (stokes/min)',spm,mode='larger',
debug=False,smoothwindow=2.,
activewindow=[activesecondsmin,activesecondsmax],
)
except:
print('mies')
messages.error(request,'Error updating SPM')
intervalstats = rowdata.allstats() intervalstats = rowdata.allstats()
@@ -5822,6 +5886,9 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
'value_power': power, 'value_power': power,
'value_pace': pace_secs, 'value_pace': pace_secs,
'value_work': work, 'value_work': work,
'value_spm': spm,
'activeminutesmin': activeminutesmin,
'activeminutesmax': activeminutesmax,
} }
powerupdateform = PowerIntervalUpdateForm(initial=cd) powerupdateform = PowerIntervalUpdateForm(initial=cd)
form = SummaryStringForm() form = SummaryStringForm()
@@ -5888,7 +5955,10 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
'power': int(normp), 'power': int(normp),
'pace': avpace, 'pace': avpace,
'selector': 'power', 'selector': 'power',
'work': int(normw) 'work': int(normw),
'spm': int(normspm),
'activeminutesmin': 0,
'activeminutesmax': activeminutesmax,
}) })
@@ -5972,6 +6042,9 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
return render(request, 'summary_edit.html', return render(request, 'summary_edit.html',
{'form':form, {'form':form,
'activeminutesmax':activeminutesmax,
'activeminutesmin':activeminutesmin,
'maxminutes': maxminutes,
'detailform':detailform, 'detailform':detailform,
'powerupdateform':powerupdateform, 'powerupdateform':powerupdateform,
'workout':row, 'workout':row,