Private
Public Access
1
0

Merge branch 'feature/freedefaults' into develop

This commit is contained in:
Sander Roosendaal
2017-10-13 14:31:28 +02:00
17 changed files with 527 additions and 328 deletions

View File

@@ -2482,13 +2482,13 @@ def interactive_flex_chart2(id=0,promember=0,
title="Max Distance",callback=callback)
callback.args["maxdist"] = slider_dist_max
layout = layoutrow([layoutcolumn([slider_spm_min,
layout = layoutrow([layoutcolumn([annotation,
slider_spm_min,
slider_spm_max,
slider_dist_min,
slider_dist_max,
slider_work_min,
slider_work_max,
annotation,
],
),
plot])
@@ -2499,6 +2499,213 @@ def interactive_flex_chart2(id=0,promember=0,
return [script,div,js_resources,css_resources,workstrokesonly]
def thumbnails_set(r,id,favorites):
charts = []
columns = [f.xparam for f in favorites]
columns += [f.yparam1 for f in favorites]
columns += [f.yparam2 for f in favorites]
columns += ['time']
rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True)
rowdata.dropna(axis=1,how='all',inplace=True)
if rowdata.empty:
return [
{'script':"",
'div':"",
'notes':""
}]
else:
try:
rowdata.sort_values(by='time',ascending=True,inplace=True)
except KeyError:
pass
l = len(rowdata)
maxlength = 50
if l > maxlength:
bins = np.linspace(rowdata['time'].min(),rowdata['time'].max(),maxlength)
groups = rowdata.groupby(np.digitize(rowdata['time'],bins))
rowdata = groups.mean()
for f in favorites:
workstrokesonly = not f.reststrokes
script,div = thumbnail_flex_chart(
rowdata,
id=id,
xparam=f.xparam,
yparam1=f.yparam1,
yparam2=f.yparam2,
plottype=f.plottype,
)
charts.append({
'script':script,
'div':div,
'notes':f.notes})
return charts
def thumbnail_flex_chart(rowdata,id=0,promember=0,
xparam='time',
yparam1='pace',
yparam2='hr',
plottype='line',
workstrokesonly=False):
try:
tests = rowdata[yparam2]
except KeyError:
yparam2 = 'None'
try:
tests = rowdata[yparam1]
except KeyError:
yparam1 = 'None'
try:
tseconds = rowdata.ix[:,'time']
except KeyError:
return '','No time data - cannot make flex plot','',''
try:
rowdata['x1'] = rowdata.ix[:,xparam]
except KeyError:
rowdata['x1'] = 0*rowdata.ix[:,'time']
try:
rowdata['y1'] = rowdata.ix[:,yparam1]
except KeyError:
rowdata['y1'] = 0*rowdata.ix[:,'time']
if yparam2 != 'None':
try:
rowdata['y2'] = rowdata.ix[:,yparam2]
except KeyError:
rowdata['y2'] = 0*rowdata.ix[:,'time']
else:
rowdata['y2'] = rowdata['y1']
if xparam=='time':
xaxmax = tseconds.max()
xaxmin = tseconds.min()
elif xparam=='distance' or xparam=='cumdist':
xaxmax = rowdata['x1'].max()
xaxmin = rowdata['x1'].min()
else:
xaxmax = yaxmaxima[xparam]
xaxmin = yaxminima[xparam]
x_axis_type = 'linear'
y_axis_type = 'linear'
if xparam == 'time':
x_axis_type = 'datetime'
if yparam1 == 'pace':
y_axis_type = 'datetime'
y1mean = rowdata.ix[:,'pace'].mean()
rowdata['xname'] = axlabels[xparam]
rowdata['yname1'] = axlabels[yparam1]
if yparam2 != 'None':
rowdata['yname2'] = axlabels[yparam2]
else:
rowdata['yname2'] = axlabels[yparam1]
# prepare data
source = ColumnDataSource(
rowdata
)
sizing_mode = 'fixed' # 'scale_width' also looks nice with this example
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
plot_width=200,plot_height=150,
)
plot.toolbar.logo = None
plot.toolbar_location = None
#plot.yaxis.visible = False
plot.xaxis.axis_label_text_font_size = "7pt"
plot.yaxis.axis_label_text_font_size = "7pt"
plot.xaxis.major_label_text_font_size = "7pt"
plot.yaxis.major_label_text_font_size = "7pt"
if plottype=='line':
plot.line('x1','y1',source=source)
elif plottype=='scatter':
plot.scatter('x1','y1',source=source,fill_alpha=0.4,
line_color=None)
plot.xaxis.axis_label = axlabels[xparam]
plot.yaxis.axis_label = axlabels[yparam1]
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
plot.y_range = yrange1
if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'):
xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam])
plot.x_range = xrange1
if xparam == 'time':
xrange1 = Range1d(start=xaxmin,end=xaxmax)
plot.x_range = xrange1
plot.xaxis[0].formatter = DatetimeTickFormatter(
hours = ["%H"],
minutes = ["%M"],
seconds = ["%S"],
days = ["0"],
months = [""],
years = [""]
)
if yparam1 == 'pace':
plot.yaxis[0].formatter = DatetimeTickFormatter(
seconds = ["%S"],
minutes = ["%M"]
)
if yparam2 != 'None':
yrange2 = Range1d(start=yaxminima[yparam2],end=yaxmaxima[yparam2])
plot.extra_y_ranges["yax2"] = yrange2
#= {"yax2": yrange2}
if plottype=='line':
plot.line('x1','y2',color="red",y_range_name="yax2",
source=source)
elif plottype=='scatter':
plot.scatter('x1','y2',source=source,
fill_alpha=0.4,
line_color=None,color="red",y_range_name="yax2")
plot.add_layout(LinearAxis(y_range_name="yax2",
axis_label=axlabels[yparam2],
major_label_text_font_size="7pt",
axis_label_text_font_size="7pt",
),'right',
)
script, div = components(plot)
return [script,div]
def interactive_bar_chart(id=0,promember=0):
# check if valid ID exists (workout exists)

View File

@@ -219,7 +219,7 @@ rowingmetrics = (
'ax_max': 15,
'default': 0,
'mode':'both',
'type': 'pro'}),
'type': 'basic'}),
)
@@ -236,3 +236,68 @@ axlabels = {ax[0]:ax[1] for ax in axes}
yaxminima = {ax[0]:ax[2] for ax in axes}
yaxmaxima = {ax[0]:ax[3] for ax in axes}
defaultfavoritecharts = (
{
'yparam1':'pace',
'yparam2':'spm',
'xparam':'time',
'plottype':'line',
'workouttype':'both',
'reststrokes':True,
'notes':"""This chart shows your pace and stroke rate versus time. """,
},
{
'yparam1':'pace',
'yparam2':'hr',
'xparam':'time',
'plottype':'line',
'workouttype':'both',
'reststrokes':True,
'notes':"""This chart shows your pace and heart rate versus time.
Heart rate values will be shown only when it is in your data, i.e.
in case you recorded your heart rate during your workout""",
},
{
'yparam1':'distanceperstroke',
'yparam2':'hr',
'xparam':'time',
'plottype':'line',
'workouttype':'otw',
'reststrokes':True,
'notes':"""This chart shows the Distance covered per stroke, and your
heart rate versus time. """,
},
{
'yparam1':'strokeenergy',
'yparam2':'hr',
'xparam':'time',
'plottype':'line',
'workouttype':'ote',
'reststrokes':True,
'notes':"""This chart shows the Work per Stroke and your heart rate
plotted versus time. """,
},
{
'yparam1':'distanceperstroke',
'yparam2':'None',
'xparam':'spm',
'plottype':'scatter',
'workouttype':'otw',
'reststrokes':True,
'notes':"""This chart shows the Distance per Stroke versus stroke
stroke rate. You should see a steady decline of the Distance per Stroke
as you increase stroke rate. Typical values are > 10m for steady state
dropping to 8m for race pace in the single.""",
},
{
'yparam1':'strokeenergy',
'yparam2':'None',
'xparam':'spm',
'plottype':'line',
'workouttype':'ote',
'reststrokes':True,
'notes':"""This chart shows the Work per Stroke versus Stroke Rate.
This value should be fairly constant across all stroke rates.""",
},
)

View File

@@ -298,6 +298,10 @@ class Rower(models.Model):
defaulttimezone = models.CharField(default='UTC',max_length=100,
choices=timezones,
verbose_name='Default Time Zone')
# Show flex chart notes
showfavoritechartnotes = models.BooleanField(default=True,
verbose_name='Show Notes for Favorite Charts')
def __str__(self):
return self.user.username
@@ -344,6 +348,8 @@ class FavoriteChart(models.Model):
default='both',
verbose_name='Workout Type')
reststrokes = models.BooleanField(default=True,verbose_name="Incl. Rest")
notes = models.CharField(max_length=300,verbose_name='Chart Notes',
default='Flex Chart Notes',blank=True)
user = models.ForeignKey(Rower)
@@ -351,7 +357,11 @@ class FavoriteForm(ModelForm):
class Meta:
model = FavoriteChart
fields = ['xparam','yparam1','yparam2',
'plottype','workouttype','reststrokes']
'plottype','workouttype','reststrokes','notes']
# widgets = {
# 'notes': forms.Textarea,
# }
# To generate favorite chart forms on the fly
class BaseFavoriteFormSet(BaseFormSet):
@@ -828,7 +838,8 @@ class RowerPowerZonesForm(ModelForm):
class AccountRowerForm(ModelForm):
class Meta:
model = Rower
fields = ['weightcategory','getemailnotifications','defaulttimezone']
fields = ['weightcategory','getemailnotifications',
'defaulttimezone','showfavoritechartnotes']
class UserForm(ModelForm):
class Meta:

View File

@@ -5,6 +5,11 @@
{% block content %}
<form method="post">
{% csrf_token %}
<div class="grid_12 alpha">
<div class="grid_4 fav-form-header">
<p><input type="submit" value="Update Favorites" class="button green small"/></p>
</div>
</div>
{{ favorites_formset.management_form }}
{% for favorites_form in favorites_formset %}
@@ -18,11 +23,6 @@
<div class="grid_12 alpha">
<p>&nbsp;</p>
</div>
<div class="grid_12 alpha">
<div class="grid_2">
<p><input type="submit" value="Update Favorites" class="button green small"/></p>
</div>
</div>
</form>
@@ -37,4 +37,5 @@
});
</script>
{% endblock %}

View File

@@ -1,95 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %} Flexible Plot {% endblock %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<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">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/time/{{ yparam1 }}/{{ yparam2 }}">Time</a>
</div>
<div class="grid_2">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/distance/{{ yparam1 }}/{{ yparam2 }}">Distance</a>
</div>
<div class="grid_2 omega">
<a class="button blue small"
href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam2 }}/{{ yparam1 }}">Swap Y axes</a>
</div>
</div>
<div id="y-axis" class="grid_6 omega">
<div class="grid_1 prefix_2 alpha">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/pace">Pace</a>
</div>
<div class="grid_1">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/hr">HR</a>
</div>
<div class="grid_1">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/spm">SPM</a>
</div>
<div class="grid_1 omega">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/power">Power</a>
</div>
</div>
</div>
<div id="theplot" class="grid_12 alpha">
{{ the_div|safe }}
</div>
{% endblock %}

View File

@@ -1,210 +0,0 @@
{% 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>
{% 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>
{% 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 }}/rhythm/{{ yparam2 }}/{{ plottype }}">Stroke Rhythm</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">Stroke Rhytm (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 }}/rhythm/{{ plottype }}">Drive Rhythm</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">Drive Rhythm (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">
<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 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>
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<div id="favorites" class="grid_12 alpha">
<div class="grid_2 suffix_4 alpha">
{% if maxfav >= 0 %}
<a class="button gray small" href="/rowers/me/favoritecharts">Manage Favorites</a>
{% else %}
&nbsp;
{% endif %}
</div>
<div class="grid_1">
{% if favoritenr > 0 %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart={{ favoritenr|add:-1 }}">&lt</a>
{% else %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart={{ maxfav }}">&lt</a>
{% endif %}
</div>
<div class="grid_2">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% csrf_token %}
<input class="grid_2 alpha button blue small" type="hidden" name="savefavorite" value="True">
{% if workstrokesonly %}
<input type="hidden" name="workstrokesonlysave" value="False">
{% else %}
<input type="hidden" name="workstrokesonlysave" value="True">
{% endif %}
<input class="grid_2 alpha button blue small" value="Make Favorite" type="Submit">
</form>
</div>
<div class="grid_1">
{% if favoritenr < maxfav %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart={{ favoritenr|add:1 }}">&gt</a>
{% else %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart=0">&gt</a>
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}
{% endlocaltime %}

View File

@@ -151,7 +151,6 @@
</div>
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<div id="favorites" class="grid_12 alpha">
<div class="grid_2 suffix_4 alpha">
{% if maxfav >= 0 %}
@@ -187,7 +186,6 @@
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}
{% endlocaltime %}

View File

@@ -0,0 +1,14 @@
{% if charts %}
<h2>Flex Charts</h2>
{% for chart in charts %}
<div class="grid_3 alpha">
<big>{{ forloop.counter }}</big>
<div class="grid_3 tooltip">
<a href="/rowers/workout/{{ workout.id }}/flexchart?favoritechart={{ forloop.counter |add:"-1" }}">
{{ chart.div | safe }}
</a>
<span class="tooltiptext">{{ chart.notes }}</span>
</div>
</div>
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,18 @@
<div class="grid_2 alpha">
<h2>Navigation</h2>
</div>
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced</a>
</p>
</div>
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/editintervals">Edit Intervals</a>
</p>
</div>

View File

@@ -0,0 +1,11 @@
{% if statcharts %}
<h2>Static Charts</h2>
{% for graph in statcharts %}
<div id="thumb-container" class="grid_3 alpha">
<a href="/rowers/graph/{{ graph.id }}/">
<img src="/{{ graph.filename }}"
onerror="this.src='/static/img/waiting.png'"
alt="{{ graph.filename }}" width="180" height="150"></a>
</div>
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,23 @@
<div class="grid_2 alpha">
<h2>Create Charts</h2>
</div>
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addtimeplot">Add Time Plot</a>
</p>
</div>
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/adddistanceplot">Add Distance Plot</a>
</p>
</div>
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addpiechart">Add Pie Chart</a>
</p>
</div>
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addpowerpiechart">Add Power Pie Chart</a>
</p>
</div>

View File

@@ -0,0 +1,5 @@
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/stats">Workout Stats</a>
</p>
</div>

View File

@@ -0,0 +1,6 @@
<h2>Summary</h2>
<p>
<pre>
{{ workout.summary }}
</pre>
</p>

View File

@@ -0,0 +1,56 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% load tz %}
{% get_current_timezone as TIME_ZONE %}
{% block title %}{{ workout.name }}{% endblock %}
{% block og_title %}{{ workout.name }}{% endblock %}
{% block description %}{{ workout.name }}
{{ workout.date }} - {{ workout.distance }}m - {{ workout.duration |durationprint:"%H:%M:%S.%f" }}{% endblock %}
{% block og_description %}{{ workout.name }}
{{ workout.date }} - {{ workout.distance }}m - {{ workout.duration |durationprint:"%H:%M:%S.%f" }}{% endblock %}
{% block meta %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{% for chart in charts %}
{{ chart.script |safe }}
{% endfor %}
{% endblock %}
{% block content %}
<div id="page" class="grid_12 alpha">
<div class="grid_10 prefix_2 alpha">
<h1>{{ workout.name }}</h1>
</div>
<div id="leftpanel" class="grid_2 alpha">
{% block left_panel %}
{% for templateName in leftTemplates %}
{% include templateName %}
{% endfor %}
{% endblock %}
<div class="grid_2 alpha">
<p>Click on the thumbnails to view the full chart</p>
</div>
</div>
<div id="middlepanel" class="grid_9">
{% block middle_panel %}
{% for templateName in middleTemplates %}
<div class="grid_9">
{% include templateName %}
</div>
{% endfor %}
{% endblock %}
</div>
<div id="rightpanel" class="grid_1">
{% block right_panel %}
<p>&nbsp;</p>
{% endblock %}
</div>
{% endblock %}

View File

@@ -327,6 +327,7 @@ urlpatterns = [
url(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'),
url(r'^register$',views.rower_register_view),
url(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'),
url(r'^workout/(?P<id>\d+)/workflow$',views.workout_workflow_view),
url(r'^workout/(?P<id>\d+)/flexchart/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/(?P<plottype>\w+)/$',views.workout_flexchart3_view),
url(r'^workout/(?P<id>\d+)/flexchart/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/(?P<plottype>\w+.*)$',views.workout_flexchart3_view),
url(r'^workout/(?P<id>\d+)/flexchart/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)$',views.workout_flexchart3_view),

View File

@@ -49,7 +49,7 @@ from rowers.models import (
WorkoutComment,WorkoutCommentForm,RowerExportForm,
)
from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement
from rowers.metrics import rowingmetrics
from rowers.metrics import rowingmetrics,defaultfavoritecharts
import rowers.uploads as uploads
from django.forms.formsets import formset_factory
import StringIO
@@ -324,6 +324,22 @@ def ispromember(user):
result = False
return result
# More User/Rower utils
def add_defaultfavorites(r):
for c in defaultfavoritecharts:
f = FavoriteChart(user=r,
yparam1=c['yparam1'],
yparam2=c['yparam2'],
xparam=c['xparam'],
plottype=c['plottype'],
workouttype=c['workouttype'],
reststrokes=c['reststrokes'],
notes=c['notes'])
f.save()
return 1
# User registration
def rower_register_view(request):
if request.method == 'POST':
@@ -344,6 +360,9 @@ def rower_register_view(request):
therower.save()
# create default favorite charts
add_defaultfavorites(therower)
# Create Sample workout
f = 'media/testdata.csv.gz'
timestr = strftime("%Y%m%d-%H%M%S")
@@ -5827,7 +5846,62 @@ def workout_comparison_view2(request,id1=0,id2=0,xparam='distance',
'promember':promember,
})
# Flex thumbnails
@login_required()
def workout_workflow_view(request,id):
request.session['referer'] = absolute(request)['PATH']
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
try:
row = Workout.objects.get(id=id)
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
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
workouttype = 'ote'
if row.workouttype in ('water','coastal'):
workouttype = 'otw'
try:
favorites = FavoriteChart.objects.filter(user=r,
workouttype__in=[workouttype,'both']).order_by("id")
maxfav = len(favorites)-1
except:
favorites = None
maxfav = 0
charts = []
if favorites:
charts = thumbnails_set(r,id,favorites)
statcharts = GraphImage.objects.filter(workout=row)
# This will be user configurable in the future
middleTemplates = ['panel_statcharts.html','flexthumbnails.html',
'panel_summary.html']
leftTemplates = [
'panel_editbuttons.html',
'panel_stats.html',
'panel_staticchart.html',
]
return render(request,
'workflow.html',
{
'middleTemplates':middleTemplates,
'leftTemplates':leftTemplates,
'charts':charts,
'workout':row,
'statcharts':statcharts,
})
# The famous flex chart
def workout_flexchart3_view(request,*args,**kwargs):
@@ -5959,12 +6033,6 @@ def workout_flexchart3_view(request,*args,**kwargs):
css_resources = ""
# script = res[0]
# div = res[1]
# js_resources = res[2]
# css_resources = res[3]
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"]
@@ -6745,7 +6813,10 @@ def workout_add_chart_view(request,id,plotnr=1):
imagename=imagename)
url = "/rowers/workout/"+str(w.id)+"/edit"
try:
url = request.session['referer']
except KeyError:
url = "/rowers/workout/"+str(w.id)+"/edit"
return HttpResponseRedirect(url)
@@ -8258,7 +8329,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
})
# Page where user can manage his favorite charts
@user_passes_test(ispromember,login_url="/rowers/me/edit",redirect_field_name=None)
@login_required()
def rower_favoritecharts_view(request):
message = ''
successmessage = ''

View File

@@ -62,11 +62,28 @@ h1 {
font-weight: normal;
/* padding-top: 20px; */
text-align: left;
font-size: 2em;
}
h2 {
/* padding-top: 20px; */
/* padding-top: 20px; */
font-weight: normal;
text-align: left;
font-size: 1.5em;
}
h3 {
/* padding-top: 20px; */
font-weight: normal;
text-align: left;
font-size: 1em;
}
h3 {
/* padding-top: 20px; */
font-weight: normal;
text-align: left;
font-size: 1em;
}
p {