Private
Public Access
1
0

Merge branch 'develop' into feature/restapi

This commit is contained in:
Sander Roosendaal
2016-12-01 14:37:01 +01:00
6 changed files with 308 additions and 20 deletions

View File

@@ -350,6 +350,16 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
driveenergy = rowdatadf.ix[:,'driveenergy']
drivelength = driveenergy/(averageforce*4.44822)
slip = rowdatadf.ix[:,'slip']
if windowsize > 3:
wash = savgol_filter(wash,windowsize,3)
slip = savgol_filter(slip,windowsize,3)
catch = savgol_filter(catch,windowsize,3)
finish = savgol_filter(finish,windowsize,3)
peakforce = savgol_filter(peakforce,windowsize,3)
averageforce = savgol_filter(averageforce,windowsize,3)
peakforceangle = savgol_filter(peakforceangle,windowsize,3)
driveenergy = savgol_filter(driveenergy,windowsize,3)
drivelength = savgol_filter(drivelength,windowsize,3)
data['wash'] = wash
data['catch'] = catch
data['slip'] = slip

View File

@@ -885,6 +885,11 @@ def interactive_flex_chart2(id=0,promember=0,
'forceratio': 'Average/Peak Drive Force Ratio',
'driveenergy': 'Work per Stroke (J)',
'drivespeed': 'Drive Speed (m/s)',
'slip': 'Slip (degrees)',
'catch': 'Catch (degrees)',
'finish': 'Finish (degrees)',
'wash': 'Wash (degrees)',
'peakforceangle': 'Peak Force Angle (degrees)',
'None': '',
}
@@ -899,12 +904,17 @@ def interactive_flex_chart2(id=0,promember=0,
'drivelength':0.5,
'driveenergy': 0,
'drivespeed': 0,
'slip': 0,
'catch': -70,
'finish': 30,
'wash': 0,
'peakforceangle': -20,
}
yaxmaxima = {
'hr':200,
'spm':45,
'pace': 1.0e3*90,
'pace': 1.0e3*75,
'power': 600,
'averageforce':200,
'peakforce':400,
@@ -912,9 +922,20 @@ def interactive_flex_chart2(id=0,promember=0,
'drivelength':2.0,
'driveenergy': 1000,
'drivespeed':4,
'slip': 30,
'catch': -30,
'finish': 70,
'wash': 30,
'peakforceangle': 20,
}
rowdata,row = dataprep.getrowdata_db(id=id)
#columns = [xparam,yparam1,yparam2,
# 'ftime','distance','fpace',
# 'power','hr','spm',
# 'time','pace']
#rowdata = dataprep.getsmallrowdata_db(columns,ids=[id])
#row = Workout.objects.get(id=id)
if rowdata.empty:
return "","CSV Data File Not Found"

View File

@@ -94,14 +94,18 @@ and inspired by the RowPro Dan Burpee spreadsheet
<p>Beyond the basics, rowsandall.com provides a rich tool set for
OTW rowing. You can use it to estimate stroke power and to correct
for the effects of wind and current. For indoor rowing, when used
with painsled, the tools enable graphical analsis of stroke metrics
for the effects of wind and current. For indoor rowing, when
used with painsled, and OTW rowing, when used
with a NK Empower Oarlock,
the tools enable graphical analsis of stroke metrics
including:
<ul>
<li> Peak and average force</li>
<li> drive length</li>
<li> work per stroke</li>
<li> drive and recovery time</li>
<li> catch and finish angles (OTW)</li>
<li> slip and wash (OTW)</li>
</ul>
These can be analyzed versus time, distance, stroke rate or other
@@ -138,6 +142,8 @@ You will be taken to the secure PayPal payment site.
<p>
<ul>
<li>2016-12-01 Support for NK Empower Oarlock parameters (catch and
finish angles, slip and wash, and power as measured by the Oarlock</li>
<li>2016-11-10 Power based Pie Charts</li>
<li>2016-11-01 Sliders to select subsets of data on some plots</li>
<li>2016-11-01 Emailing workouts to workouts@rowsandall.com </li>
@@ -155,7 +161,7 @@ You will be taken to the secure PayPal payment site.
<li>2016-06-08 Added possibility to upload CrewNerd summary CSV file for Premium Members</li>
<li>2016-06-08 Added workout summaries</li>
<li>2016-06-05 Export to Strava is working</li>
<li>2016-06-01 We're approved on the Concept2 logbook!!!!
<li>2016-06-01 We're approved on the Concept2 logbook!!!!</li>
</ul>
</p>

View File

@@ -0,0 +1,203 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% load tz %}
{% block title %} Flexible Plot {% endblock %}
{% localtime on %}
{% block content %}
{{ js_res | safe }}
{{ css_res| safe }}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_8 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
</div>
<p>&nbsp;</p>
<div id="plotbuttons" class="grid_12 alpha">
<div id="x-axis" class="grid_6 alpha">
<div class="grid_2 alpha dropdown">
<button class="grid_2 alpha button blue small dropbtn">X-axis</button>
<div class="dropdown-content">
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/time/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Time</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/distance/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Distance</a>
{% if promember %}
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/power/{{ yparam1 }}/{{ yparam2 }}/scatter">Power</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/hr/{{ yparam1 }}/{{ yparam2 }}/scatter">HR</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/spm/{{ yparam1 }}/{{ yparam2 }}/scatter">SPM</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/peakforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Peak Force</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/averageforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Average Force</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/forceratio/{{ yparam1 }}/{{ yparam2 }}/scatter">Average/Peak force ratio</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/drivelength/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Length</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/driveenergy/{{ yparam1 }}/{{ yparam2 }}/scatter">Work per Stroke</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/drivespeed/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Speed</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/catch/{{ yparam1 }}/{{ yparam2 }}/scatter">Catch Angle</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/finish/{{ yparam1 }}/{{ yparam2 }}/scatter">Finish Angle</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/slip/{{ yparam1 }}/{{ yparam2 }}/scatter">Slip</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/wash/{{ yparam1 }}/{{ yparam2 }}/scatter">Wash</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/peakforceangle/{{ yparam1 }}/{{ yparam2 }}/scatter">Peak Force Angle</a>
{% else %}
<a class="button rosy small" href="/rowers/promembership">Power (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">HR (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">SPM (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average/Peak Force Ratio (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Catch Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Finish Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Slip (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Wash (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Peak Force Angle (Pro)</a>
{% endif %}
</div>
</div>
<div class="grid_2 dropdown">
<button class="grid_2 alpha button blue small dropbtn">Left</button>
<div class="dropdown-content">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/pace/{{ yparam2 }}/{{ plottype }}">Pace</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/hr/{{ yparam2 }}/{{ plottype }}">HR</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/spm/{{ yparam2 }}/{{ plottype }}">SPM</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/power/{{ yparam2 }}/{{ plottype }}">Power</a>
{% if promember %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/peakforce/{{ yparam2 }}/{{ plottype }}">Peak Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/averageforce/{{ yparam2 }}/{{ plottype }}">Average Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/forceratio/{{ yparam2 }}/{{ plottype }}">Average/Peak Force Ratio</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/drivelength/{{ yparam2 }}/{{ plottype }}">Drive Length</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/driveenergy/{{ yparam2 }}/{{ plottype }}">Work per Stroke</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/drivespeed/{{ yparam2 }}/{{ plottype }}">Drive Speed</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/catch/{{ yparam2 }}/{{ plottype }}">Catch Angle</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/finish/{{ yparam2 }}/{{ plottype }}">Finish Angle</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/slip/{{ yparam2 }}/{{ plottype }}">Slip</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/wash/{{ yparam2 }}/{{ plottype }}">Wash</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/peakforceangle/{{ yparam2 }}/{{ plottype }}">Peak Force Angle</a>
{% else %}
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average/Peak Force Ratio (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Catch Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Finish Angle(Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Wash (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Slip (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Peak Force Angle (Pro)</a>
{% endif %}
</div>
</div>
<div class="grid_2 dropdown omega">
<button class="grid_2 alpha button blue small dropbtn">Right</button>
<div class="dropdown-content">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/hr/{{ plottype }}">HR</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/spm/{{ plottype }}">SPM</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/power/{{ plottype }}">Power</a>
{% if promember %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/peakforce/{{ plottype }}">Peak Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/averageforce/{{ plottype }}">Average Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/forceratio/{{ plottype }}">Average/Peak Force Ratio</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/drivelength/{{ plottype }}">Drive Length</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/driveenergy/{{ plottype }}">Work per Stroke</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/drivespeed/{{ plottype }}">Drive Speed</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/catch/{{ plottype }}">Catch Angle</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/finish/{{ plottype }}">Finish Angle</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/slip/{{ plottype }}">Slip</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/wash/{{ plottype }}">Wash</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/peakforceangle/{{ plottype }}">Peak Force Angle</a>
{% else %}
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average/Peak Force Ratio (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Catch Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Finish Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Slip (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Wash (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Peak Force Angle (Pro)</a>
{% endif %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/None/{{ plottype }}">None</a>
</div>
</div>
</div>
<div id="y-axis" class="grid_6 omega">
<div class="grid_2 alpha tooltip">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% csrf_token %}
{% if workstrokesonly %}
<input type="hidden" name="workstrokesonly" value="True">
{% else %}
<input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="False">
{% endif %}
<input class="grid_2 alpha button blue small" value="Toggle Work Strokes" type="Submit">
</form>
<span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span>
</div>
<div class="grid_2">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/line">Line Plot</a>
</div>
<div class="grid_2 omega">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/scatter">Scatter Plot</a>
</div>
</div>
</div>
<div id="theplot" class="grid_12 alpha">
{{ the_div|safe }}
</div>
{% endblock %}
{% endlocaltime %}

View File

@@ -690,6 +690,37 @@ class ViewTest(TestCase):
os.remove(f_to_be_deleted)
def test_upload_view_SpeedCoach2v127intervals(self):
self.c.login(username='john',password='koeinsloot')
filename = 'C:\\python\\rowingdata\\testdata\\SpeedCoach2Link_interval.csv'
f = open(filename,'rb')
file_data = {'file': f}
form_data = {
'title':'test',
'workouttype':'water',
'notes':'aap noot mies',
'make_plot':False,
'upload_to_c2':False,
'plottype':'timeplot',
'file': f,
}
form = DocumentsForm(form_data,file_data)
response = self.c.post('/rowers/workout/upload/', form_data, follow=True)
f.close()
self.assertRedirects(response, expected_url='/rowers/workout/1/edit',
status_code=302,target_status_code=200)
self.assertEqual(response.status_code, 200)
w = Workout.objects.get(id=1)
f_to_be_deleted = w.csvfilename
os.remove(f_to_be_deleted)
def test_upload_view_TCX_NoHR(self):
self.c.login(username='john',password='koeinsloot')

View File

@@ -2611,6 +2611,23 @@ def workout_flexchart3_view(request,id=0,xparam='distance',yparam1='pace',
js_resources = res[2]
css_resources = res[3]
if row.workouttype == 'water':
return render(request,
'flexchart3otw.html',
{'the_script':script,
'the_div':div,
'js_res': js_resources,
'css_res':css_resources,
'id':id,
'xparam':xparam,
'yparam1':yparam1,
'yparam2':yparam2,
'plottype':plottype,
'mayedit':mayedit,
'promember':promember,
'workstrokesonly': not workstrokesonly,
})
else:
return render(request,
'flexchart3.html',
{'the_script':script,