diff --git a/rowers/forms.py b/rowers/forms.py index 3376dc36..984febfc 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -1322,7 +1322,7 @@ palettechoices = tuple((p, p) for p in palettes.keys()) analysischoices = ( ('boxplot', 'Box Chart'), - ('trendflex', 'Trend Flex'), +# ('trendflex', 'Trend Flex'), ('histo', 'Histogram'), ('flexall', 'Cumulative Flex Chart'), ('stats', 'Statistics'), diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index df9a7bca..d3136a4e 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -4454,22 +4454,19 @@ def interactive_flex_chart2(id, r, promember=0, trendline=False, mode='rower'): - watermarkurl = "/static/img/logo7.png" - watermarkrange = Range1d(start=0, end=1) - watermarkalpha = 0.6 - watermarkx = 0.99 - watermarky = 0.01 - watermarkw = 184 - watermarkh = 35 - watermarkanchor = 'bottom_right' - columns = [xparam, yparam1, yparam2, - 'ftime', 'distance', 'fpace', - 'power', 'hr', 'spm', 'driveenergy', - 'time', 'pace', 'workoutstate'] + columns = [name for name, d in metrics.rowingmetrics] + columns_basic = [name for name, d in metrics.rowingmetrics if d['group'] == 'basic'] + columns = columns + ['spm', 'driveenergy', 'distance'] + columns_basic = columns_basic + ['spm', 'driveenergy', 'distance'] - rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=True, - workstrokesonly=workstrokesonly) + datadf = pd.DataFrame() + if promember: + rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=True, + workstrokesonly=workstrokesonly, for_chart=True) + else: + rowdata = dataprep.getsmallrowdata_db(columns_basic, ids=[id], doclean=True, + workstrokesonly=workstrokesonly, for_chart=True) if r.usersmooth > 1: # pragma: no cover for column in columns: @@ -4484,10 +4481,14 @@ def interactive_flex_chart2(id, r, promember=0, try: if len(rowdata) < 2: - rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], - doclean=False, - workstrokesonly=False) - workstrokesonly = False + if promember: + rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], + doclean=False, + workstrokesonly=False, for_chart=True) + else: + rowdata = dataprep.getsmallrowdata_db(columns_basic, ids=[id], doclean=False, + workstrokesonly=False, for_chart=True) + workstrokesonly = False except (KeyError, TypeError): # pragma: no cover workstrokesonly = False try: @@ -4517,7 +4518,7 @@ def interactive_flex_chart2(id, r, promember=0, row = Workout.objects.get(id=id) if rowdata.empty: - return "", "No valid data", '', '', workstrokesonly + return "", "No valid data", workstrokesonly workoutstatesrest = [3] @@ -4530,7 +4531,7 @@ def interactive_flex_chart2(id, r, promember=0, try: tseconds = rowdata.loc[:, 'time'] except KeyError: # pragma: no cover - return '', 'No time data - cannot make flex plot', '', '', workstrokesonly + return '', 'No time data - cannot make flex plot', workstrokesonly try: rowdata['x1'] = rowdata.loc[:, xparam] @@ -4578,27 +4579,6 @@ def interactive_flex_chart2(id, r, promember=0, y1mean = rowdata['y1'].mean() y2mean = rowdata['y2'].mean() - if xparam != 'time': - xvals = xaxmin+np.arange(100)*(xaxmax-xaxmin)/100. - else: - xvals = np.arange(100) - - # constant power plot - if yparam1 == 'driveenergy': - if xparam == 'spm': # pragma: no cover - yconstantpower = rowdata['y1'].mean()*rowdata['x1'].mean()/xvals - - x_axis_type = 'linear' - y_axis_type = 'linear' - if xparam == 'time': - x_axis_type = 'datetime' - - if yparam1 == 'pace': - y_axis_type = 'datetime' - try: - y1mean = rowdata.loc[:, 'pace'].mean() - except KeyError: # pragma: no cover - y1mean = 0 try: rowdata['xname'] = axlabels[xparam] @@ -4627,420 +4607,25 @@ def interactive_flex_chart2(id, r, promember=0, rowdata['ytrend'] = ytrend except TypeError: # pragma: no cover rowdata['ytrend'] = y1 - - # prepare data - source = ColumnDataSource( - rowdata - ) - # second source for filtering - source2 = ColumnDataSource( - rowdata.copy() - ) + data_dict = rowdata.to_dict("records") - # Add hover to this comma-separated string and see what changes - if (promember == 1): - TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - else: - TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' + metrics_list = [{'name': name, 'rowingmetrics':d } for name, d in metrics.rowingmetrics] + + chart_data = { + 'title': row.name, + 'x': xparam, + 'y1': yparam1, + 'y2': yparam2, + 'data': data_dict, + 'metrics': metrics_list, + 'trendline': trendline, + 'plottype': plottype, + } - plot = figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type, - tools=TOOLS, toolbar_location='above', - toolbar_sticky=False, width=800, height=600, - ) - #plot.sizing_mode = 'stretch_both' + script, div = get_chart("/flex", chart_data) - # add watermark - watermarkurl = "/static/img/logo7.png" - watermarkrange = Range1d(start=0, end=1) - watermarkalpha = 0.6 - watermarkx = 0.99 - watermarky = 0.01 - watermarkw = 184 - watermarkh = 35 - watermarkanchor = 'bottom_right' - plot.extra_y_ranges = {"watermark": watermarkrange} - plot.extra_x_ranges = {"watermark": watermarkrange} - #plot.sizing_mode = 'stretch_both' - - plot.image_url([watermarkurl], watermarkx, watermarky, - watermarkw, watermarkh, - global_alpha=watermarkalpha, - w_units='screen', - h_units='screen', - anchor=watermarkanchor, - dilate=True, - x_range_name="watermark", - y_range_name="watermark", - ) - - x1means = Span(location=x1mean, dimension='height', line_color='green', - line_dash=[6, 6], line_width=2) - - y1means = Span(location=y1mean, dimension='width', line_color='blue', - line_dash=[6, 6], line_width=2) - y2means = y1means - - try: - xlabeltext = axlabels[xparam]+": {x1mean:6.2f}".format( - x1mean=x1mean - ) - except KeyError: # pragma: no cover - xlabeltext = xparam+": {x1mean:6.2f}".format(x1mean=x1mean) - - xlabel = Label(x=50, y=80, x_units='screen', y_units='screen', - text=xlabeltext, - background_fill_alpha=.7, - background_fill_color='white', - text_color='green', - ) - - annolabel = Label(x=50, y=450, x_units='screen', y_units='screen', - text='', - background_fill_alpha=0.7, - background_fill_color='white', - text_color='black', - ) - - sliderlabel = Label(x=10, y=470, x_units='screen', y_units='screen', - text='', - background_fill_alpha=0.7, - background_fill_color='white', - text_color='black', text_font_size='10pt', - ) - - if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover - plot.add_layout(x1means) - plot.add_layout(xlabel) - - plot.add_layout(y1means) - plot.add_layout(annolabel) - plot.add_layout(sliderlabel) - - try: - yaxlabel = axlabels[yparam1] - except KeyError: # pragma: no cover - yaxlabel = str(yparam1)+' ' - - try: - xaxlabel = axlabels[xparam] - except KeyError: # pragma: no cover - xaxlabel = xparam - - y1label = Label(x=50, y=50, x_units='screen', y_units='screen', - text=yaxlabel+": {y1mean:6.2f}".format(y1mean=y1mean), - background_fill_alpha=.7, - background_fill_color='white', - text_color='blue', - ) - if yparam1 != 'time' and yparam1 != 'pace': # pragma: no cover - plot.add_layout(y1label) - y2label = y1label - - # average values - if yparam1 == 'driveenergy': # pragma: no cover - if xparam == 'spm': - plot.line(xvals, yconstantpower, color="green", - legend_label="Constant Power") - - # trendline - if trendline: # pragma: no cover - plot.line('x1', 'ytrend', source=source2, legend_label=yaxlabel+' (trend)') - - if plottype == 'line': - plot.line('x1', 'y1', source=source2, legend_label=yaxlabel) - elif plottype == 'scatter': # pragma: no cover - plot.scatter('x1', 'y1', source=source2, legend_label=yaxlabel, fill_alpha=0.4, - line_color=None) - - try: - plot.title.text = row.name - except ValueError: # pragma: no cover - plot.title.text = "" - plot.title.text_font_size = "1.0em" - - #plot.sizing_mode = 'stretch_both' - plot.xaxis.axis_label = xaxlabel - - plot.yaxis.axis_label = yaxlabel - - try: - yrange1 = Range1d(start=get_yaxminima(r, yparam1, mode), - end=get_yaxmaxima(r, yparam1, mode)) - except KeyError: # pragma: no cover - yrange1 = Range1d(start=rowdata[yparam1].min(), - end=rowdata[yparam1].max()) - - plot.y_range = yrange1 - - if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover - try: - xrange1 = Range1d(start=get_yaxminima(r, xparam, mode), - end=get_yaxmaxima(r, xparam, mode)) - except KeyError: - xrange1 = Range1d(start=rowdata[xparam].min(), - end=rowdata[xparam].max()) - - 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': - try: - yrange2 = Range1d(start=get_yaxminima(r, yparam2, mode), - end=get_yaxmaxima(r, yparam2, mode)) - except KeyError: # pragma: no cover - yrange2 = Range1d(start=rowdata[yparam2].min(), - end=rowdata[yparam2].max()) - - plot.extra_y_ranges["yax2"] = yrange2 - # = {"yax2": yrange2} - try: - axlegend = axlabels[yparam2] - except KeyError: # pragma: no cover - axlegend = str(yparam2)+' ' - - if plottype == 'line': - plot.line('x1', 'y2', color="red", y_range_name="yax2", - legend_label=axlegend, - source=source2) - - elif plottype == 'scatter': # pragma: no cover - plot.scatter('x1', 'y2', source=source2, legend_label=axlegend, - fill_alpha=0.4, - line_color=None, color="red", y_range_name="yax2") - - plot.add_layout(LinearAxis(y_range_name="yax2", - axis_label=axlegend), 'right') - - y2means = Span(location=y2mean, dimension='width', line_color='red', - line_dash=[6, 6], line_width=2, y_range_name="yax2") - - plot.add_layout(y2means) - y2label = Label(x=50, y=20, x_units='screen', y_units='screen', - text=axlegend+": {y2mean:6.2f}".format(y2mean=y2mean), - background_fill_alpha=.7, - background_fill_color='white', - text_color='red', - ) - if yparam2 != 'pace' and yparam2 != 'time': - plot.add_layout(y2label) - - hover = plot.select(dict(type=HoverTool)) - - hover.tooltips = OrderedDict([ - ('Time', '@ftime'), - ('Distance', '@distance{int}'), - ('Pace', '@fpace'), - ('HR', '@hr{int}'), - ('SPM', '@spm{1.1}'), - ('Power', '@power{int}'), - ]) - - hover.mode = 'mouse' - - callback = CustomJS(args=dict(source=source, source2=source2, - x1means=x1means, - y1means=y1means, - y1label=y1label, - y2label=y2label, - xlabel=xlabel, - annolabel=annolabel, - sliderlabel=sliderlabel, - y2means=y2means, - ), code=""" - var data = source.data - var data2 = source2.data - var x1 = data['x1'] - var y1 = data['y1'] - var y2 = data['y2'] - var spm1 = data['spm'] - var time1 = data['time'] - var ftime1 = data['ftime'] - var pace1 = data['pace'] - var hr1 = data['hr'] - var fpace1 = data['fpace'] - var distance1 = data['distance'] - var power1 = data['power'] - var driveenergy1 = data['driveenergy'] - var xname = data['xname'] - var yname1 = data['yname1'] - var yname2 = data['yname2'] - var workoutid1 = data['workoutid'] - var workoutstate1 = data['workoutstate'] - var ytrend = data['ytrend'] - - var annotation = annotation.value - var minspm = minspm.value - var maxspm = maxspm.value - var mindist = mindist.value - var maxdist = maxdist.value - var minwork = minwork.value - var maxwork = maxwork.value - - sliderlabel.text = 'SPM: '+minspm.toFixed(0)+'-'+maxspm.toFixed(0) - sliderlabel.text += ', Dist: '+mindist.toFixed(0)+'-'+maxdist.toFixed(0) - sliderlabel.text += ', WpS: '+minwork.toFixed(0)+'-'+maxwork.toFixed(0) - - var xm = 0 - var ym1 = 0 - var ym2 = 0 - - data2['x1'] = [] - data2['y1'] = [] - data2['y2'] = [] - data2['spm'] = [] - data2['time'] = [] - data2['ftime'] = [] - data2['pace'] = [] - data2['hr'] = [] - data2['fpace'] = [] - data2['distance'] = [] - data2['power'] = [] - data2['x1mean'] = [] - data2['y1mean'] = [] - data2['y2mean'] = [] - data2['driveenergy'] = [] - data2['workoutid'] = [] - data2['workoutstate'] = [] - data2['xname'] = [] - data2['yname1'] = [] - data2['yname2'] = [] - data2['ytrend'] = [] - - - for (var i=0; i=minspm && spm1[i]<=maxspm) { - if (distance1[i]>=mindist && distance1[i]<=maxdist) { - if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) { - data2['x1'].push(x1[i]) - data2['y1'].push(y1[i]) - data2['y2'].push(y2[i]) - data2['spm'].push(spm1[i]) - data2['time'].push(time1[i]) - data2['ftime'].push(ftime1[i]) - data2['fpace'].push(fpace1[i]) - data2['driveenergy'].push(driveenergy1[i]) - data2['pace'].push(pace1[i]) - data2['hr'].push(hr1[i]) - data2['distance'].push(distance1[i]) - data2['power'].push(power1[i]) - data2['workoutid'].push(0) - data2['workoutstate'].push(0) - data2['xname'].push(0) - data2['yname1'].push(0) - data2['yname2'].push(0) - data2['ytrend'].push(ytrend[i]) - - - xm += x1[i] - ym1 += y1[i] - ym2 += y2[i] - } - } - } - } - - xm /= data2['x1'].length - ym1 /= data2['x1'].length - ym2 /= data2['x1'].length - - for (var i=0; i - - - -{{ the_script |safe }}

{% if workout|previousworkout:rower.user %} @@ -35,9 +25,7 @@

  • -
    - {{ the_div|safe }} -
    + {{ the_div|safe }}
+ + +{{ the_script |safe }} + {% endblock %} {% endlocaltime %} diff --git a/rowers/templates/menu_workout.html b/rowers/templates/menu_workout.html index 6d8ff847..49c9f361 100644 --- a/rowers/templates/menu_workout.html +++ b/rowers/templates/menu_workout.html @@ -101,11 +101,6 @@  Force Curve -
  • - -  Corrected Pace Plot - -
  • {% endif %} @@ -304,6 +299,11 @@  Stream +
  • + +  Corrected Pace Plot + +
  •  OTW Power diff --git a/rowers/templates/user_analysis_select.html b/rowers/templates/user_analysis_select.html index c45daa2f..840f799b 100644 --- a/rowers/templates/user_analysis_select.html +++ b/rowers/templates/user_analysis_select.html @@ -388,24 +388,6 @@ -
    -
    -
    - - - - - -
    - -
    @@ -439,144 +421,144 @@ {{ chartform.as_table }}
  • -
  • -

    {{ searchform }}

    - - {% if workouts %} - - Toggle All
    - - {{ form.as_table }} -
    - {% else %} -

    No workouts found

    - {% endif %} -
  • -
  • - {% csrf_token %} - - +
  • +

    {{ searchform }}

    + + {% if workouts %} + + Toggle All
    + + {{ form.as_table }} +
    + {% else %} +

    No workouts found

    + {% endif %}
  • - {% if worldclass %} - {% if age and sex != 'not specified' %} -
  • -

    Gold Medal Standards

    -

    The dashed lines are based on the - Concept2 - rankings for your age ({{ age }}), gender ({{ sex }}) - and weight category ({{ weightcategory }}). The Gold Medal - Standard power is derived from the - - World Record for your category. - The percentile lines are estimates of where the percentiles - of the Concept2 rankings historically are for those of exactly - your age, gender and weight class. -

    -

    - For rowing on the water, the results are corrected for the expected - difference in power between the Concept2 indoor rower and power - values on the water. -

    -
  • -
  • - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - 100m - -
    - 500m - -
    - 1000m - -
    - 2000m - -
    - 5000m - -
    - 6000m - -
    - 10000m - -
    - Half Marathon - -
    - Full Marathon - -
    - 1 minute - -
    - 4 minutes - -
    - 30 minutes - -
    - 1 hour - -
    -
  • - {% endif %} - {% endif %} +
  • + {% csrf_token %} + + +
  • +{% if worldclass %} +{% if age and sex != 'not specified' %} +
  • +

    Gold Medal Standards

    +

    The dashed lines are based on the + Concept2 + rankings for your age ({{ age }}), gender ({{ sex }}) + and weight category ({{ weightcategory }}). The Gold Medal + Standard power is derived from the + + World Record for your category. + The percentile lines are estimates of where the percentiles + of the Concept2 rankings historically are for those of exactly + your age, gender and weight class. +

    +

    + For rowing on the water, the results are corrected for the expected + difference in power between the Concept2 indoor rower and power + values on the water. +

    +
  • +
  • + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + 100m + +
    + 500m + +
    + 1000m + +
    + 2000m + +
    + 5000m + +
    + 6000m + +
    + 10000m + +
    + Half Marathon + +
    + Full Marathon + +
    + 1 minute + +
    + 4 minutes + +
    + 30 minutes + +
    + 1 hour + +
    +
  • +{% endif %} +{% endif %} diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 852c308c..a09bba7b 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4367,7 +4367,7 @@ def workout_flexchart3_view(request, *args, **kwargs): # create interactive plot ( - script, div, js_resources, css_resources, workstrokesonly + script, div, workstrokesonly ) = interactive_flex_chart2( encoder.decode_hex(id), request.user.rower, xparam=xparam, yparam1=yparam1, @@ -4453,8 +4453,6 @@ def workout_flexchart3_view(request, *args, **kwargs): 'workout': row, 'chartform': flexaxesform, 'optionsform': flexoptionsform, - 'js_res': js_resources, - 'css_res': css_resources, 'teams': get_my_teams(request.user), 'id': id, 'xparam': xparam,