diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index cccd3486..9946f22c 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -284,7 +284,7 @@ def interactive_hr_piechart(df, rower, title, totalseconds=0): frac_an = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr datadict = { - '<{ut2}'.format(ut2=hrzones[1]): frac_lut2, + '<{ut2}'.format(ut2=hrzones[1]): frac_lut2, '{ut2}'.format(ut2=hrzones[1]): frac_ut2, '{ut1}'.format(ut1=hrzones[2]): frac_ut1, '{at}'.format(at=hrzones[3]): frac_at, @@ -299,7 +299,7 @@ def interactive_hr_piechart(df, rower, title, totalseconds=0): data['angle'] = data['value']/data['value'].sum() * 2*pi data['color'] = colors data['zone'] = [ - '<{ut2}'.format(ut2=hrzones[1]), + '<{ut2}'.format(ut2=hrzones[1]), '{ut2}'.format(ut2=hrzones[1]), '{ut1}'.format(ut1=hrzones[2]), '{at}'.format(at=hrzones[3]), @@ -309,24 +309,14 @@ def interactive_hr_piechart(df, rower, title, totalseconds=0): data['totaltime'] = pd.Series([pretty_timedelta(v) for v in data['value']]) - TOOLS = 'save,hover' - - z = figure(title="HR "+title, x_range=(-0.5, 1), height=375, - tools=TOOLS, toolbar_location=None, tooltips="@zone: @totaltime", - ) - - z.wedge(x=0, y=1, radius=0.4, - start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'), - line_color='white', fill_color='color', source=data, legend_group='zone') - - z.axis.axis_label = None - z.axis.visible = False - z.grid.grid_line_color = None - z.outline_line_color = None - z.toolbar_location = 'right' - - return components(z) + data_dict = data.to_dict("records") + chart_data = { + 'data': data_dict, + 'title': "HR "+ title + } + script, div = get_chart("/hrpie", chart_data, debug=True) + return script, div def pretty_timedelta(secs): hours, remainder = divmod(secs, 3600) diff --git a/rowers/templates/disqualification_view.html b/rowers/templates/disqualification_view.html index dfacbe67..5a6406cb 100644 --- a/rowers/templates/disqualification_view.html +++ b/rowers/templates/disqualification_view.html @@ -102,14 +102,11 @@ {% endif %}
-
| Name | {{ workout.name }} | -||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Distance: | {{ workout.distance }}m | -||||||||||
| Duration: | {{ workout.duration |durationprint:"%H:%M:%S.%f" }} | -||||||||||
| Public link to this workout | -
+
| + |
- Use "Interval Shorthand", "Feeling lucky?", or "Intervals by power/pace" methods below to edit work and rest - intervals. To save your work, press the Save button above the updated summary. + Use "Interval Shorthand", "Feeling lucky?", or "Intervals by power/pace" methods below to edit work and rest + intervals. To save your work, press the Save button above the updated summary.
-
- {{ intervalstats }}
-
-
- This new, experimental feature tries to find intervals by looking for patterns in the data. It does not - autodetect rest and work intervals yet. The resulting "interval string" is copied to the Interval Shorthand - section below, where you can edit it. -
- -+
+ {{ intervalstats }}
+
+
+ This new, experimental feature tries to find intervals by looking for patterns in the data. It does not + autodetect rest and work intervals yet. The resulting "interval string" is copied to the Interval Shorthand + section below, where you can edit it. +
+ +See the how-to at the bottom of this page for details on how to use this form. @@ -125,26 +122,26 @@ {% csrf_token %} -
With this form, you can specify a power or pace level. Everything faster/harder than the specified pace/power will become a work interval. Everything slower will become a rest interval. Use the slider to limit the active range. Everything outside the active range will - become rest (warming up and cooling down). + become rest (warming up and cooling down).
- + @@ -153,11 +150,11 @@This is a quick way to enter the intervals using a special mini-language.
You enter something like 8x500m/3min, press "Update" and the site will interpret this for you and update the summary on the right. If you're happy with the result, press the green Save button to update the values. Nothing will be changed permanently until you hit Save.
- +Special characters are x (times), + and / (denotes a rest interval), as well as ( and ). Units are min (minutes), sec (seconds), m (meters) and km (km).
- +A typical interval is described as "10min/5min", with the work part before the "/" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "1km" or "1000m". The basic units can be combined with "+" and "Nx". You can use parentheses as in the example below.
- +Here are a few examples
- This functionality allows you to record a time on a set course that you've rowed during the workout. - The summary will be updated to show time on course, and you can compare this with other - attempts. -
-- {% if rower.share_course_results %} - You are currently sharing your course results with all Rowsandall users. - Click here to hide your course results. - {% else %} - You are currently hiding your course results (except for your participation in online challenges). - Click here to hide your course results. - {% endif %} -
- -+ This functionality allows you to record a time on a set course that you've rowed during the workout. + The summary will be updated to show time on course, and you can compare this with other + attempts. +
++ {% if rower.share_course_results %} + You are currently sharing your course results with all Rowsandall users. + Click here to hide your course results. + {% else %} + You are currently hiding your course results (except for your participation in online challenges). + Click here to hide your course results. + {% endif %} +
+ +No ranking pieces found.
' - paulslope = 1 - paulintercept = 1 - p1 = [1, 1, 1, 1] - message = "" - - if request.method == 'POST' and "piece" in request.POST: # pragma: no cover - form = PredictedPieceForm(request.POST) - if form.is_valid(): - value = form.cleaned_data['value'] - hourvalue, value = divmod(value, 60) - if hourvalue >= 24: - hourvalue = 23 - pieceunit = form.cleaned_data['pieceunit'] - if pieceunit == 'd': - rankingdistances.append(value) - else: - rankingdurations.append(datetime.time( - minute=int(value), hour=int(hourvalue))) - else: - form = PredictedPieceForm() - - rankingdistances.sort() - rankingdurations.sort() - - predictions = [] - cpredictions = [] - - for rankingdistance in rankingdistances: - # Paul's model - p = paulslope*np.log10(rankingdistance)+paulintercept - velo = 500./p - t = rankingdistance/velo - pwr = 2.8*(velo**3) - try: - pwr = int(pwr) - except (ValueError, AttributeError): # pragma: no cover - pwr = 0 - - a = {'distance': rankingdistance, - 'duration': timedeltaconv(t), - 'pace': timedeltaconv(p), - 'power': int(pwr)} - predictions.append(a) - - # CP model - - pwr2 = p1[0]/(1+t/p1[2]) - pwr2 += p1[1]/(1+t/p1[3]) - - if pwr2 <= 0: # pragma: no cover - pwr2 = 50. - - velo2 = (pwr2/2.8)**(1./3.) - - if np.isnan(velo2) or velo2 <= 0: # pragma: no cover - velo2 = 1.0 - - t2 = rankingdistance/velo2 - - pwr3 = p1[0]/(1+t2/p1[2]) - pwr3 += p1[1]/(1+t2/p1[3]) - - if pwr3 <= 0: # pragma: no cover - pwr3 = 50. - - velo3 = (pwr3/2.8)**(1./3.) - if np.isnan(velo3) or velo3 <= 0: # pragma: no cover - velo3 = 1.0 - - t3 = rankingdistance/velo3 - p3 = 500./velo3 - - a = {'distance': rankingdistance, - 'duration': timedeltaconv(t3), - 'pace': timedeltaconv(p3), - 'power': int(pwr3)} - cpredictions.append(a) - - for rankingduration in rankingdurations: - t = 3600.*rankingduration.hour - t += 60.*rankingduration.minute - t += rankingduration.second - t += rankingduration.microsecond/1.e6 - - # Paul's model - ratio = paulintercept/paulslope - - u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope - - d = 500*t*np.log(10.) - d = d/(paulslope*lambertw(u)) - d = d.real - - velo = d/t - p = 500./velo - pwr = 2.8*(velo**3) - try: - a = {'distance': int(d), - 'duration': timedeltaconv(t), - 'pace': timedeltaconv(p), - 'power': int(pwr)} - predictions.append(a) - except: # pragma: no cover - pass - - # CP model - pwr = p1[0] / (1 + t / p1[2]) - pwr += p1[1] / (1 + t / p1[3]) - - if pwr <= 0: # pragma: no cover - pwr = 50. - - velo = (pwr / 2.8)**(1. / 3.) - - if np.isnan(velo) or velo <= 0: # pragma: no cover - velo = 1.0 - - d = t * velo - p = 500. / velo - a = {'distance': int(d), - 'duration': timedeltaconv(t), - 'pace': timedeltaconv(p), - 'power': int(pwr)} - cpredictions.append(a) - - if recalc: - wcdurations = [] - wcpower = [] - durations = [1, 4, 30, 60] - distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195] - - df = pd.DataFrame( - list( - C2WorldClassAgePerformance.objects.filter( - sex=r.sex, - weightcategory=r.weightcategory - ).values() - ) - ) - - jsondf = df.to_json() - - job = myqueue(queue, - handle_getagegrouprecords, - jsondf, distances, durations, age, r.sex, r.weightcategory) - try: - request.session['async_tasks'] += [(job.id, 'agegrouprecords')] - except KeyError: - request.session['async_tasks'] = [(job.id, 'agegrouprecords')] - - messages.error(request, message) - return render(request, 'rankings.html', - {'rankingworkouts': theworkouts, - 'interactiveplot': script, - 'the_div': div, - 'predictions': predictions, - 'cpredictions': cpredictions, - 'nrdata': len(thedistances), - 'form': form, - 'dateform': dateform, - 'deltaform': deltaform, - 'id': theuser, - 'theuser': uu, - 'rower': r, - 'active': 'nav-analysis', - 'age': age, - 'sex': r.sex, - 'recalc': recalc, - 'weightcategory': r.weightcategory, - 'startdate': startdate, - 'enddate': enddate, - 'teams': get_my_teams(request.user), - }) - - @login_required() def otecp_toadmin_view(request, theuser=0, startdate=timezone.now() - datetime.timedelta(days=365), diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index edf0b572..7fb0c248 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -630,50 +630,6 @@ def otw_use_gps(request, id=0): return HttpResponseRedirect(url) -# Show Stroke power histogram for a workout -@login_required() -@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) -def workout_histo_view(request, id=0): - w = get_workoutuser(id, request) - r = getrequestrower(request) - - mayedit = 0 - if w.user == r: - mayedit = 1 - - res = interactive_histoall([w], 'power', False) - script = res[0] - div = res[1] - - breadcrumbs = [ - { - 'url': '/rowers/list-workouts/', - 'name': 'Workouts' - }, - { - 'url': get_workout_default_page(request, id), - 'name': w.name - }, - { - 'url': reverse('workout_histo_view', kwargs={'id': id}), - 'name': 'Histogram' - } - - ] - - return render(request, - 'histo_single.html', - {'interactiveplot': script, - 'breadcrumbs': breadcrumbs, - 'active': 'nav-workouts', - 'workout': w, - 'rower': r, - 'the_div': div, - 'id': id, - 'mayedit': mayedit, - 'teams': get_my_teams(request.user), - }) - # add a workout manually @login_required()