diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 7e828b40..2cc207c0 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -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) diff --git a/rowers/metrics.py b/rowers/metrics.py index b4140f1c..b2986565 100644 --- a/rowers/metrics.py +++ b/rowers/metrics.py @@ -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.""", + }, + ) diff --git a/rowers/models.py b/rowers/models.py index 6bb2c91e..83e699e3 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -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: diff --git a/rowers/templates/favoritecharts.html b/rowers/templates/favoritecharts.html index 0ce6512d..efe81047 100644 --- a/rowers/templates/favoritecharts.html +++ b/rowers/templates/favoritecharts.html @@ -5,6 +5,11 @@ {% block content %}
@@ -37,4 +37,5 @@ }); + {% endblock %} diff --git a/rowers/templates/flexchart.html b/rowers/templates/flexchart.html deleted file mode 100644 index 479cc35b..00000000 --- a/rowers/templates/flexchart.html +++ /dev/null @@ -1,95 +0,0 @@ -{% extends "base.html" %} -{% load staticfiles %} -{% load rowerfilters %} - -{% block title %} Flexible Plot {% endblock %} - -{% block content %} - - - - - {{ interactiveplot |safe }} - - - - - - - -- - - -
- - - -
+ Edit Workout +
++ Advanced +
++ Edit Intervals +
++ Add Time Plot +
++ Add Pie Chart +
++ Workout Stats +
++
+ {{ workout.summary }}
+
+
diff --git a/rowers/templates/workflow.html b/rowers/templates/workflow.html
new file mode 100644
index 00000000..a7de9675
--- /dev/null
+++ b/rowers/templates/workflow.html
@@ -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 %}
+
+
+
+{% for chart in charts %}
+{{ chart.script |safe }}
+{% endfor %}
+{% endblock %}
+
+{% block content %}
+Click on the thumbnails to view the full chart
++ {% endblock %} +