Private
Public Access
1
0

Merge branch 'release/tcx_st'

This commit is contained in:
Sander Roosendaal
2017-01-05 18:35:41 +01:00
9 changed files with 316 additions and 159 deletions
+13 -2
View File
@@ -125,7 +125,7 @@ def tailwind(bearing,vwind,winddir):
from rowers.dataprep import nicepaceformat,niceformat from rowers.dataprep import nicepaceformat,niceformat
from rowers.dataprep import timedeltaconv from rowers.dataprep import timedeltaconv
def interactive_forcecurve(theworkouts): def interactive_forcecurve(theworkouts,workstrokesonly=False):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair' TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
ids = [int(w.id) for w in theworkouts] ids = [int(w.id) for w in theworkouts]
@@ -133,10 +133,21 @@ def interactive_forcecurve(theworkouts):
boattype = theworkouts[0].boattype boattype = theworkouts[0].boattype
columns = ['catch','slip','wash','finish','averageforce', columns = ['catch','slip','wash','finish','averageforce',
'peakforceangle','peakforce','spm','distance'] 'peakforceangle','peakforce','spm','distance',
'workoutstate']
rowdata = dataprep.getsmallrowdata_db(columns,ids=ids) rowdata = dataprep.getsmallrowdata_db(columns,ids=ids)
workoutstateswork = [1,4,5,8,9,6,7]
workoutstatesrest = [3]
workoutstatetransition = [0,2,10,11,12,13]
if workstrokesonly:
try:
rowdata = rowdata[~rowdata['workoutstate'].isin(workoutstatesrest)]
except KeyError:
pass
catchav = rowdata['catch'].mean() catchav = rowdata['catch'].mean()
finishav = rowdata['finish'].mean() finishav = rowdata['finish'].mean()
washav = rowdata['wash'].mean() washav = rowdata['wash'].mean()
+15 -1
View File
@@ -1,5 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from rowers.models import Workout,Rower,StrokeData from rowers.models import Workout,Rower,StrokeData,FavoriteChart
import datetime import datetime
@@ -8,6 +8,7 @@ class RowerSerializer(serializers.HyperlinkedModelSerializer):
class Meta: class Meta:
model = Rower model = Rower
fields = ( fields = (
'id',
'weightcategory', 'weightcategory',
'max', 'max',
'rest', 'rest',
@@ -19,6 +20,19 @@ class RowerSerializer(serializers.HyperlinkedModelSerializer):
'ftp', 'ftp',
) )
class FavoriteChartSerializer(serializers.ModelSerializer):
class Meta:
model = FavoriteChart
fields = (
'id',
'xparam',
'yparam1',
'yparam2',
'plottype',
'workouttype',
'reststrokes',
'user'
)
class WorkoutSerializer(serializers.ModelSerializer): class WorkoutSerializer(serializers.ModelSerializer):
class Meta: class Meta:
+6 -1
View File
@@ -181,8 +181,10 @@ def createsporttracksworkoutdata(w):
maxhr = int(row.df[' HRCur (bpm)'].max()) maxhr = int(row.df[' HRCur (bpm)'].max())
# adding diff, trying to see if this is valid # adding diff, trying to see if this is valid
t = row.df.ix[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)'] #t = row.df.ix[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)']
t = row.df.ix[:,'TimeStamp (sec)'].values-row.df.ix[0,'TimeStamp (sec)']
t[0] = t[1] t[0] = t[1]
d = row.df.ix[:,'cum_dist'].values d = row.df.ix[:,'cum_dist'].values
d[0] = d[1] d[0] = d[1]
t = t.astype(int) t = t.astype(int)
@@ -196,9 +198,12 @@ def createsporttracksworkoutdata(w):
try: try:
lat = row.df[' latitude'].values lat = row.df[' latitude'].values
lon = row.df[' longitude'].values lon = row.df[' longitude'].values
if not lat.std() and not lon.std():
haslatlon = 0
except KeyError: except KeyError:
haslatlon = 0 haslatlon = 0
haspower = 1 haspower = 1
try: try:
power = row.df[' Power (watts)'].values power = row.df[' Power (watts)'].values
+133 -132
View File
@@ -8,159 +8,160 @@
{% localtime on %} {% localtime on %}
{% block content %} {% block content %}
{{ js_res | safe }} {{ js_res | safe }}
{{ css_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-0.12.3.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-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"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
{{ the_script |safe }} {{ the_script |safe }}
<style> <style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/ /* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;} html, body {height: 100%; margin:5px;}
</style> </style>
<div id="navigation" class="grid_12 alpha"> <div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %} {% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha"> <div class="grid_2 alpha">
<p> <p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a> <a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p> </p>
</div> </div>
<div class="grid_2 suffix_8 omega"> <div class="grid_2 suffix_8 omega">
<p> <p>
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a> <a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
</p> </p>
</div> </div>
{% endif %} {% endif %}
</div> </div>
<p>&nbsp;</p> <p>&nbsp;</p>
<div id="plotbuttons" class="grid_12 alpha"> <div id="plotbuttons" class="grid_12 alpha">
<div id="x-axis" class="grid_6 alpha">
<div id="x-axis" class="grid_6 alpha"> <div class="grid_2 alpha dropdown">
<div class="grid_2 alpha dropdown"> <button class="grid_2 alpha button blue small dropbtn">X-axis</button>
<button class="grid_2 alpha button blue small dropbtn">X-axis</button> <div class="dropdown-content">
<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/time/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Time</a> <a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/distance/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Distance</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/distance/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Distance</a> {% if promember %}
{% 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/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/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/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/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/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/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/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/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/drivespeed/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Speed</a> {% else %}
{% else %} <a class="button rosy small" href="/rowers/promembership">Power (Pro)</a>
<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">HR (Pro)</a> <a class="button rosy small" href="/rowers/promembership">SPM (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">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 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">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">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">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">Drive Speed (Pro)</a>
{% endif %}
{% endif %} </div>
</div> </div>
</div>
<div class="grid_2 dropdown">
<div class="grid_2 dropdown"> <button class="grid_2 alpha button blue small dropbtn">Left</button>
<button class="grid_2 alpha button blue small dropbtn">Left</button> <div class="dropdown-content">
<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 }}/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 }}/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 }}/spm/{{ yparam2 }}/{{ plottype }}">SPM</a> <a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/power/{{ yparam2 }}/{{ plottype }}">Power</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/power/{{ yparam2 }}/{{ plottype }}">Power</a> {% if promember %}
{% 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 }}/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 }}/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 }}/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 }}/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 }}/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 }}/drivespeed/{{ yparam2 }}/{{ plottype }}">Drive Speed</a> {% else %}
{% else %} <a class="button rosy small" href="/rowers/promembership">Peak Force (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 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">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">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">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">Drive Speed (Pro)</a> {% endif %}
{% endif %}
</div>
</div> </div>
</div>
<div class="grid_2 dropdown omega">
<div class="grid_2 dropdown omega"> <button class="grid_2 alpha button blue small dropbtn">Right</button>
<button class="grid_2 alpha button blue small dropbtn">Right</button> <div class="dropdown-content">
<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 }}/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 }}/spm/{{ plottype }}">SPM</a> <a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/power/{{ plottype }}">Power</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/power/{{ plottype }}">Power</a> {% if promember %}
{% 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 }}/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 }}/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 }}/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 }}/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 }}/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 }}/drivespeed/{{ plottype }}">Drive Speed</a> {% else %}
{% else %} <a class="button rosy small" href="/rowers/promembership">Peak Force (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 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">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">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">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">Drive Speed (Pro)</a> {% endif %}
{% endif %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/None/{{ plottype }}">None</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/None/{{ plottype }}">None</a>
</div>
</div> </div>
</div>
</div>
</div>
<div id="y-axis" class="grid_6 omega">
<div id="y-axis" class="grid_6 omega"> <div class="grid_2 alpha tooltip">
<div class="grid_2 alpha tooltip"> <form enctype="multipart/form-data" action="{{ formloc }}" method="post">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% csrf_token %} {% csrf_token %}
{% if workstrokesonly %} {% if workstrokesonly %}
<input type="hidden" name="workstrokesonly" value="True"> <input type="hidden" name="workstrokesonly" value="True">
<input class="grid_2 alpha button blue small" value="Remove Rest Strokes" type="Submit">
{% else %} {% else %}
<input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="False"> <input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="False">
<input class="grid_2 alpha button blue small" value="Include Rest Strokes" type="Submit">
{% endif %} {% endif %}
<input class="grid_2 alpha button blue small" value="Toggle Work Strokes" type="Submit"> </form>
</form> <span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span>
<span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span> </div>
</div> <div class="grid_2">
<div class="grid_2"> <a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/line">Line Plot</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/line">Line Plot</a> </div>
</div> <div class="grid_2 omega">
<div class="grid_2 omega"> <a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/scatter">Scatter Plot</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/scatter">Scatter Plot</a> </div>
</div> </div>
</div>
</div> </div>
<div id="theplot" class="grid_12 alpha"> <div id="theplot" class="grid_12 alpha">
{{ the_div|safe }} {{ the_div|safe }}
</div> </div>
{% if user.rower.rowerplan == 'pro' %} {% if user.rower.rowerplan == 'pro' %}
+2 -1
View File
@@ -174,10 +174,11 @@
{% csrf_token %} {% csrf_token %}
{% if workstrokesonly %} {% if workstrokesonly %}
<input type="hidden" name="workstrokesonly" value="True"> <input type="hidden" name="workstrokesonly" value="True">
<input class="grid_2 alpha button blue small" value="Remove Rest Strokes" type="Submit">
{% else %} {% else %}
<input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="False"> <input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="False">
<input class="grid_2 alpha button blue small" value="Include Rest Strokes" type="Submit">
{% endif %} {% endif %}
<input class="grid_2 alpha button blue small" value="Toggle Work Strokes" type="Submit">
</form> </form>
<span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span> <span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span>
</div> </div>
+28 -13
View File
@@ -8,22 +8,22 @@
{% localtime on %} {% localtime on %}
{% block content %} {% block content %}
{{ js_res | safe }} {{ js_res | safe }}
{{ css_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-0.12.3.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-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"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
{{ the_script |safe }} {{ the_script |safe }}
<style> <style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/ /* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;} html, body {height: 100%; margin:5px;}
</style> </style>
<div id="navigation" class="grid_12 alpha"> <div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %} {% if user.is_authenticated and mayedit %}
@@ -32,13 +32,28 @@
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a> <a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p> </p>
</div> </div>
<div class="grid_2 suffix_8 omega"> <div class="grid_2 suffix_2">
<p> <p>
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a> <a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
</p> </p>
</div> </div>
{% endif %} {% endif %}
<div class="grid_2 suffix_4 omega tooltip">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% csrf_token %}
{% if workstrokesonly %}
<input type="hidden" name="workstrokesonly" value="True">
<input class="grid_2 alpha button blue small" value="Remove Rest Strokes" type="Submit">
{% else %}
<input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="False">
<input class="grid_2 alpha button blue small" value="Include Rest Strokes" type="Submit">
{% endif %}
</form>
<span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span>
</div>
</div> </div>
+27 -2
View File
@@ -9,7 +9,7 @@
</p> </p>
{% endif %} {% endif %}
<div class="grid_6 alpha"> <div class="grid_6 alpha">
<h1>Heart Rate Bands</h1> <h2>Heart Rate Bands</h2>
<form enctype="multipart/form-data" action="" method="post"> <form enctype="multipart/form-data" action="" method="post">
<table> <table>
@@ -22,8 +22,8 @@
</div> </div>
</div> </div>
<div class="grid_6 omega"> <div class="grid_6 omega">
<h1>Functional Threshold Power</h1>
<p> <p>
<h2>Functional Threshold Power</h2>
<form enctype="multipart/form-data" action="" method="post"> <form enctype="multipart/form-data" action="" method="post">
<table> <table>
{{ powerform.as_table }} {{ powerform.as_table }}
@@ -34,6 +34,31 @@
</form> </form>
</div> </div>
</p> </p>
{% if grants %}
<p>
<h2>Applications</h2>
<table width="100%">
<thead>
<tr>
<th>Application</th>
<th>Scope</th>
<th>Revoke</th>
</tr>
</thead>
<tbody>
{% for grant in grants %}
<tr>
<td>{{ grant.application }}</td>
<td>{{ grant.scope }}</td>
<td>
<a class="button red small" href="/rowers/me/revokeapp/{{ grant.application.id }}">Revoke</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</p>
{% endif %}
</div> </div>
+46 -2
View File
@@ -2,7 +2,7 @@ from django.conf import settings
from django.conf.urls import url, include from django.conf.urls import url, include
from django.contrib.auth.models import User from django.contrib.auth.models import User
from models import Workout,Rower,StrokeData from models import Workout,Rower,StrokeData,FavoriteChart
from rest_framework import routers, serializers, viewsets,permissions from rest_framework import routers, serializers, viewsets,permissions
from rest_framework.urlpatterns import format_suffix_patterns from rest_framework.urlpatterns import format_suffix_patterns
@@ -19,8 +19,18 @@ from rowers.serializers import (
WorkoutSerializer, WorkoutSerializer,
RowerSerializer, RowerSerializer,
StrokeDataSerializer, StrokeDataSerializer,
FavoriteChartSerializer,
) )
from oauth2_provider.views import (
AuthorizedTokensListView,
AuthorizedTokenDeleteView,
)
from oauth2_provider.views.base import (
RevokeTokenView
)
class WorkoutViewSet(viewsets.ModelViewSet): class WorkoutViewSet(viewsets.ModelViewSet):
model = Workout model = Workout
#queryset = Workout.objects.all().order_by("-date", "-starttime") #queryset = Workout.objects.all().order_by("-date", "-starttime")
@@ -36,12 +46,45 @@ class WorkoutViewSet(viewsets.ModelViewSet):
IsOwnerOrNot, IsOwnerOrNot,
) )
class RowerViewSet(viewsets.ModelViewSet):
model = Rower
serializer_class = RowerSerializer
#queryset = Rower.objects.all()
def get_queryset(self):
r = Rower.objects.filter(user=self.request.user)
return r
permission_classes = (
IsOwnerOrNot,
)
http_method_names = ['get','patch']
class FavoriteChartViewSet(viewsets.ModelViewSet):
model = FavoriteChart
serializer_class = FavoriteChartSerializer
#queryset = FavoriteChart.objects.all()
def get_queryset(self):
r = Rower.objects.filter(user=self.request.user)
return FavoriteChart.objects.filter(user=r)
permission_classes = (
IsOwnerOrNot,
)
http_method_names = ['get','put','patch','delete']
class StrokeDataViewSet(viewsets.ModelViewSet): class StrokeDataViewSet(viewsets.ModelViewSet):
serializer_class = StrokeDataSerializer serializer_class = StrokeDataSerializer
# Routers provide an easy way of automatically determining the URL conf. # Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r'api/workouts',WorkoutViewSet, 'workout') router.register(r'api/workouts',WorkoutViewSet, 'workout')
router.register(r'api/me',RowerViewSet, 'rower')
router.register(r'api/charts',FavoriteChartViewSet, 'charts')
handler500 = 'views.error500_view' handler500 = 'views.error500_view'
@@ -159,6 +202,7 @@ urlpatterns = [
url(r'^me/edit/$',views.rower_edit_view), url(r'^me/edit/$',views.rower_edit_view),
url(r'^me/edit/(.+.*)/$',views.rower_edit_view), url(r'^me/edit/(.+.*)/$',views.rower_edit_view),
url(r'^me/c2authorize/$',views.rower_c2_authorize), url(r'^me/c2authorize/$',views.rower_c2_authorize),
url(r'^me/revokeapp/(\d+)$',views.rower_revokeapp_view),
url(r'^me/stravaauthorize/$',views.rower_strava_authorize), url(r'^me/stravaauthorize/$',views.rower_strava_authorize),
url(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize), url(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize),
url(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh), url(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh),
+46 -5
View File
@@ -75,6 +75,8 @@ import stravalib
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
from weather import get_wind_data from weather import get_wind_data
from oauth2_provider.models import Application,Grant,AccessToken
import django_rq import django_rq
queue = django_rq.get_queue('default') queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low') queuelow = django_rq.get_queue('low')
@@ -857,7 +859,7 @@ def workout_tcxemail_view(request,id=0):
}) })
except: except:
successmessage = "" successmessage = ""
message = "Something went wrong (strava export) "+str(sys.exc_info()[0]) message = "Something went wrong (TCX export) "+str(sys.exc_info()[0])
with open("media/c2errors.log","a") as errorlog: with open("media/c2errors.log","a") as errorlog:
errorstring = str(sys.exc_info()[0]) errorstring = str(sys.exc_info()[0])
timestr = strftime("%Y%m%d-%H%M%S") timestr = strftime("%Y%m%d-%H%M%S")
@@ -1076,7 +1078,6 @@ def workout_sporttracks_upload_view(request,id=0):
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json" url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data))
# check for duplicate error first # check for duplicate error first
if (response.status_code == 409 ): if (response.status_code == 409 ):
message = "Duplicate error" message = "Duplicate error"
@@ -1091,7 +1092,7 @@ def workout_sporttracks_upload_view(request,id=0):
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
else: else:
s = response s = response
message = "Something went wrong in workout_sporttracks_upload_view %s" % s message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason
else: else:
message = "You are not authorized to upload this workout" message = "You are not authorized to upload this workout"
@@ -1467,7 +1468,7 @@ def cum_flex(request,theuser=0,
}) })
@user_passes_test(promember,login_url="/",redirect_field_name=None) @user_passes_test(promember,login_url="/",redirect_field_name=None)
def workout_forcecurve_view(request,id=0): def workout_forcecurve_view(request,id=0,workstrokesonly=False):
row = Workout.objects.get(id=id) row = Workout.objects.get(id=id)
promember=0 promember=0
mayedit=0 mayedit=0
@@ -1482,7 +1483,15 @@ def workout_forcecurve_view(request,id=0):
if not promember: if not promember:
return HttpResponseRedirect("/rowers/about/") return HttpResponseRedirect("/rowers/about/")
script,div,js_resources,css_resources = interactive_forcecurve([row]) if request.method == 'POST' and 'workstrokesonly' in request.POST:
workstrokesonly = request.POST['workstrokesonly']
if workstrokesonly == 'True':
workstrokesonly = True
else:
workstrokesonly = False
script,div,js_resources,css_resources = interactive_forcecurve([row],
workstrokesonly=workstrokesonly)
return render(request, return render(request,
'forcecurve_single.html', 'forcecurve_single.html',
@@ -1493,6 +1502,7 @@ def workout_forcecurve_view(request,id=0):
'css_res':css_resources, 'css_res':css_resources,
'id':id, 'id':id,
'mayedit':mayedit, 'mayedit':mayedit,
'workstrokesonly': not workstrokesonly,
}) })
@login_required() @login_required()
@@ -3765,6 +3775,13 @@ def workout_upload_view(request,message=""):
args=[str(message)]) args=[str(message)])
response = HttpResponseRedirect(url) response = HttpResponseRedirect(url)
return response return response
if fileformat == 'rowprolog':
message = "This RowPro logbook summary does not contain stroke data. Please use the Stroke Data CSV file for the individual workout in your log."
url = reverse(workout_upload_view,
args=[str(message)])
response = HttpResponseRedirect(url)
return response
if fileformat == 'unknown': if fileformat == 'unknown':
message = "We couldn't recognize the file type" message = "We couldn't recognize the file type"
@@ -4779,14 +4796,38 @@ def rower_edit_view(request,message=""):
r = Rower.objects.get(user=request.user) r = Rower.objects.get(user=request.user)
form = RowerForm(instance=r) form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r) powerform = RowerPowerForm(instance=r)
grants = AccessToken.objects.filter(user=request.user)
return render(request, 'rower_form.html', return render(request, 'rower_form.html',
{ {
'form':form, 'form':form,
'powerform':powerform, 'powerform':powerform,
'grants':grants,
}) })
except Rower.DoesNotExist: except Rower.DoesNotExist:
return HttpResponse("This user doesn't exist") return HttpResponse("This user doesn't exist")
@login_required()
def rower_revokeapp_view(request,id=0):
tokens = AccessToken.objects.filter(user=request.user,application=id)
refreshtokens = AccessToken.objects.filter(user=request.user,application=id)
for token in tokens:
token.revoke()
for token in refreshtokens:
token.revoke()
r = Rower.objects.get(user=request.user)
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
grants = AccessToken.objects.filter(user=request.user)
return render(request, 'rower_form.html',
{
'form':form,
'powerform':powerform,
'grants':grants,
})
class JSONResponse(HttpResponse): class JSONResponse(HttpResponse):
def __init__(self, data, **kwargs): def __init__(self, data, **kwargs):
content = JSONRenderer().render(data) content = JSONRenderer().render(data)