hr pie v1
This commit is contained in:
@@ -284,7 +284,7 @@ def interactive_hr_piechart(df, rower, title, totalseconds=0):
|
|||||||
frac_an = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr
|
frac_an = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr
|
||||||
|
|
||||||
datadict = {
|
datadict = {
|
||||||
'<{ut2}'.format(ut2=hrzones[1]): frac_lut2,
|
'<{ut2}'.format(ut2=hrzones[1]): frac_lut2,
|
||||||
'{ut2}'.format(ut2=hrzones[1]): frac_ut2,
|
'{ut2}'.format(ut2=hrzones[1]): frac_ut2,
|
||||||
'{ut1}'.format(ut1=hrzones[2]): frac_ut1,
|
'{ut1}'.format(ut1=hrzones[2]): frac_ut1,
|
||||||
'{at}'.format(at=hrzones[3]): frac_at,
|
'{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['angle'] = data['value']/data['value'].sum() * 2*pi
|
||||||
data['color'] = colors
|
data['color'] = colors
|
||||||
data['zone'] = [
|
data['zone'] = [
|
||||||
'<{ut2}'.format(ut2=hrzones[1]),
|
'<{ut2}'.format(ut2=hrzones[1]),
|
||||||
'{ut2}'.format(ut2=hrzones[1]),
|
'{ut2}'.format(ut2=hrzones[1]),
|
||||||
'{ut1}'.format(ut1=hrzones[2]),
|
'{ut1}'.format(ut1=hrzones[2]),
|
||||||
'{at}'.format(at=hrzones[3]),
|
'{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']])
|
data['totaltime'] = pd.Series([pretty_timedelta(v) for v in data['value']])
|
||||||
|
|
||||||
TOOLS = 'save,hover'
|
data_dict = data.to_dict("records")
|
||||||
|
chart_data = {
|
||||||
z = figure(title="HR "+title, x_range=(-0.5, 1), height=375,
|
'data': data_dict,
|
||||||
tools=TOOLS, toolbar_location=None, tooltips="@zone: @totaltime",
|
'title': "HR "+ title
|
||||||
)
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
script, div = get_chart("/hrpie", chart_data, debug=True)
|
||||||
|
return script, div
|
||||||
|
|
||||||
def pretty_timedelta(secs):
|
def pretty_timedelta(secs):
|
||||||
hours, remainder = divmod(secs, 3600)
|
hours, remainder = divmod(secs, 3600)
|
||||||
|
|||||||
@@ -102,14 +102,11 @@
|
|||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
|
<script src="https://d3js.org/d3.v6.js"></script>
|
||||||
<script async="true" type="text/javascript">
|
|
||||||
Bokeh.set_log_level("info");
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{ interactiveplot |safe }}
|
|
||||||
|
|
||||||
{{ the_div|safe }}
|
{{ the_div|safe }}
|
||||||
|
{{ interactiveplot |safe }}
|
||||||
|
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
<li class="grid_4">
|
<li class="grid_4">
|
||||||
|
|||||||
@@ -6,12 +6,9 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
|
<script src="https://d3js.org/d3.v6.js"></script>
|
||||||
<script async="true" type="text/javascript">
|
|
||||||
Bokeh.set_log_level("info");
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{ interactiveplot |safe }}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -24,6 +21,7 @@
|
|||||||
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
{{ interactiveplot |safe }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<div id="id_js_res">
|
<div id="id_js_res">
|
||||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
|
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
|
||||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
|
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
|
||||||
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script async="true" type="text/javascript">
|
<script async="true" type="text/javascript">
|
||||||
@@ -52,35 +53,35 @@
|
|||||||
<input type="submit" value="Submit"/>
|
<input type="submit" value="Submit"/>
|
||||||
</form>
|
</form>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
<h2>All workouts</h2>
|
<h2>All workouts</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<table class="listtable shortpadded">
|
<table class="listtable shortpadded">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Total Distance</td><td>{{ totalsdict|lookup:"distance"}} meters</td>
|
<td>Total Distance</td><td>{{ totalsdict|lookup:"distance"}} meters</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Total Duration</td><td>{{ totalsdict|lookup:"duration"}} </td>
|
<td>Total Duration</td><td>{{ totalsdict|lookup:"duration"}} </td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Number of workouts</td><td>{{ totalsdict|lookup:"nrworkouts"}}</td>
|
<td>Number of workouts</td><td>{{ totalsdict|lookup:"nrworkouts"}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Average heart rate</td><td><span id="total_hr"></span> bpm</td>
|
<td>Average heart rate</td><td><span id="total_hr"></span> bpm</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Maximum heart rate</td><td><span id="total_maxhr"></span> bpm</td>
|
<td>Maximum heart rate</td><td><span id="total_maxhr"></span> bpm</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Average power</td><td><span id="total_power"></span> W</td>
|
<td>Average power</td><td><span id="total_power"></span> W</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Maximum power</td><td><span id="total_maxpower"></span> W</td>
|
<td>Maximum power</td><td><span id="total_maxpower"></span> W</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
@@ -96,40 +97,40 @@
|
|||||||
{{ totaldiv|safe }}
|
{{ totaldiv|safe }}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{% for ddict in typedicts %}
|
{% for ddict in typedicts %}
|
||||||
<li class="grid_1">
|
<li class="grid_1">
|
||||||
<h2>{{ ddict|lookup:"wtype"}}</h2>
|
<h2>{{ ddict|lookup:"wtype"}}</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<table class="listtable shortpadded">
|
<table class="listtable shortpadded">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Total Distance</td><td>{{ ddict|lookup:"distance"}} meters</td>
|
<td>Total Distance</td><td>{{ ddict|lookup:"distance"}} meters</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Total Duration</td><td>{{ ddict|lookup:"duration"}} </td>
|
<td>Total Duration</td><td>{{ ddict|lookup:"duration"}} </td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Number of workouts</td><td>{{ ddict|lookup:"nrworkouts"}}</td>
|
<td>Number of workouts</td><td>{{ ddict|lookup:"nrworkouts"}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Average heart rate</td><td><span id="{{ ddict|lookup:'id'}}_hr"></span> bpm</td>
|
<td>Average heart rate</td><td><span id="{{ ddict|lookup:'id'}}_hr"></span> bpm</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Maximum heart rate</td><td><span id="{{ ddict|lookup:'id' }}_hrmax"></span> bpm</td>
|
<td>Maximum heart rate</td><td><span id="{{ ddict|lookup:'id' }}_hrmax"></span> bpm</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Average power</td><td><span id="{{ ddict|lookup:'id'}}_power"></span> W</td>
|
<td>Average power</td><td><span id="{{ ddict|lookup:'id'}}_power"></span> W</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Maximum power</td><td><span id="{{ ddict|lookup:'id'}}_powermax"></span> W</td>
|
<td>Maximum power</td><td><span id="{{ ddict|lookup:'id'}}_powermax"></span> W</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@@ -142,40 +143,39 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(function($) {
|
$(function($) {
|
||||||
console.log('loading script for chart');
|
console.log('loading script for chart');
|
||||||
var ed = '{{ senddate|date:"Y-m-d" }}'
|
var ed = '{{ senddate|date:"Y-m-d" }}'
|
||||||
console.log('End',ed);
|
console.log('End',ed);
|
||||||
console.log(window.location.protocol + '//'+window.location.host + '/rowers/history/user/{{ rower.user.id }}/data/?startdate={{ sstartdate|date:"Y-m-d" }}&enddate={{ senddate|date:"Y-m-d" }}&workouttype={{ workouttype }}&yaxis={{ yaxis }}')
|
console.log(window.location.protocol + '//'+window.location.host + '/rowers/history/user/{{ rower.user.id }}/data/?startdate={{ sstartdate|date:"Y-m-d" }}&enddate={{ senddate|date:"Y-m-d" }}&workouttype={{ workouttype }}&yaxis={{ yaxis }}')
|
||||||
$.getJSON(window.location.protocol + '//'+window.location.host + '/rowers/history/user/{{ rower.user.id }}/data/?startdate={{ sstartdate|date:"Y-m-d" }}&enddate={{ senddate|date:"Y-m-d" }}&workouttype={{ workouttype }}&yaxis={{ yaxis }}', function(json) {
|
$.getJSON(window.location.protocol + '//'+window.location.host + '/rowers/history/user/{{ rower.user.id }}/data/?startdate={{ sstartdate|date:"Y-m-d" }}&enddate={{ senddate|date:"Y-m-d" }}&workouttype={{ workouttype }}&yaxis={{ yaxis }}', function(json) {
|
||||||
|
|
||||||
|
|
||||||
var script = json.script;
|
var script = json.script;
|
||||||
var div = json.div;
|
var div = json.div;
|
||||||
var totalsdict = json.totalsdict
|
var totalsdict = json.totalsdict
|
||||||
var listofdicts = json.listofdicts
|
var listofdicts = json.listofdicts
|
||||||
var activities_script = json.activities_script
|
var activities_script = json.activities_script
|
||||||
var activities_chart = json.activities_chart
|
var activities_chart = json.activities_chart
|
||||||
$("#id_sitready").remove();
|
$("#id_sitready").remove();
|
||||||
$("#id_chart").append(div);
|
$("#id_chart").append(div);
|
||||||
// $("#id_script").append("<s"+"cript>"+script+"</s"+"cript>");
|
$("#id_script").append(script);
|
||||||
$("#id_script").append(script);
|
$("#id_activities").append(activities_chart)
|
||||||
$("#id_activities").append(activities_chart)
|
$("#activities_script").append(activities_script)
|
||||||
$("#activities_script").append(activities_script)
|
$("#total_hr").append(totalsdict.hrmean);
|
||||||
$("#total_hr").append(totalsdict.hrmean);
|
$("#total_maxhr").append(totalsdict.hrmax);
|
||||||
$("#total_maxhr").append(totalsdict.hrmax);
|
$("#total_power").append(totalsdict.powermean);
|
||||||
$("#total_power").append(totalsdict.powermean);
|
$("#total_maxpower").append(totalsdict.powermax);
|
||||||
$("#total_maxpower").append(totalsdict.powermax);
|
listofdicts.forEach(function(item){
|
||||||
listofdicts.forEach(function(item){
|
var id = "#"+item.id+"_hr";
|
||||||
var id = "#"+item.id+"_hr";
|
$(id).append(item.hrmean);
|
||||||
$(id).append(item.hrmean);
|
id = "#"+item.id+"_hrmax";
|
||||||
id = "#"+item.id+"_hrmax";
|
$(id).append(item.hrmax);
|
||||||
$(id).append(item.hrmax);
|
id = "#"+item.id+"_power";
|
||||||
id = "#"+item.id+"_power";
|
$(id).append(item.powermean);
|
||||||
$(id).append(item.powermean);
|
id = "#"+item.id+"_powermax";
|
||||||
id = "#"+item.id+"_powermax";
|
$(id).append(item.powermax);
|
||||||
$(id).append(item.powermax);
|
})
|
||||||
})
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
|
|
||||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
|
<script src="https://d3js.org/d3.v6.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>
|
||||||
|
|||||||
@@ -8,26 +8,26 @@
|
|||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{% include "monitorjobs.html" %}
|
{% include "monitorjobs.html" %}
|
||||||
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
||||||
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
||||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$( function() {
|
$( function() {
|
||||||
console.log({{ activeminutesmin }}, {{ activeminutesmax}}, 'active range');
|
console.log({{ activeminutesmin }}, {{ activeminutesmax}}, 'active range');
|
||||||
$( "#slider-range" ).slider({
|
$( "#slider-range" ).slider({
|
||||||
range: true,
|
range: true,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: {{ maxminutes }},
|
max: {{ maxminutes }},
|
||||||
values: [ {{ activeminutesmin }}, {{ activeminutesmax }} ],
|
values: [ {{ activeminutesmin }}, {{ activeminutesmax }} ],
|
||||||
slide: function( event, ui ) {
|
slide: function( event, ui ) {
|
||||||
$( "#amount" ).val(ui.values[ 0 ] + " min - " + ui.values[ 1 ] + " min " );
|
$( "#amount" ).val(ui.values[ 0 ] + " min - " + ui.values[ 1 ] + " min " );
|
||||||
$("#id_activeminutesmin").val(ui.values[0]);
|
$("#id_activeminutesmin").val(ui.values[0]);
|
||||||
$("#id_activeminutesmax").val(ui.values[1]);
|
$("#id_activeminutesmax").val(ui.values[1]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$( "#amount" ).val($( "#slider-range" ).slider( "values", 0 ) +
|
$( "#amount" ).val($( "#slider-range" ).slider( "values", 0 ) +
|
||||||
" min - " + $( "#slider-range" ).slider( "values", 1 ) + " min ");
|
" min - " + $( "#slider-range" ).slider( "values", 1 ) + " min ");
|
||||||
} );
|
} );
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
@@ -42,78 +42,75 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<table width=100%>
|
<table width=100%>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th><td>{{ workout.name }}</td>
|
<th>Name</th><td>{{ workout.name }}</td>
|
||||||
</tr><tr>
|
</tr><tr>
|
||||||
<th>Distance:</th><td>{{ workout.distance }}m</td>
|
<th>Distance:</th><td>{{ workout.distance }}m</td>
|
||||||
</tr><tr>
|
</tr><tr>
|
||||||
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
|
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
|
||||||
</tr><tr>
|
</tr><tr>
|
||||||
<th>Public link to this workout</th>
|
<th>Public link to this workout</th>
|
||||||
<td>
|
<td>
|
||||||
<a href="/rowers/workout/{{ workout.id|encode }}/">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}</a>
|
<a href="/rowers/workout/{{ workout.id|encode }}/">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}</a>
|
||||||
<td>
|
<td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>Edit Workout Interval Data</h1>
|
<h1>Edit Workout Interval Data</h1>
|
||||||
<p>
|
<p>
|
||||||
Use "Interval Shorthand", "Feeling lucky?", or "Intervals by power/pace" methods below to edit work and rest
|
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.
|
intervals. To save your work, press the Save button above the updated summary.
|
||||||
</p>
|
</p>
|
||||||
<ul class="main-content">
|
<ul class="main-content">
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
<h1>Updated Summary</h1>
|
<h1>Updated Summary</h1>
|
||||||
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
|
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="{{ savebutton }}" value="{{ intervalstring }}">
|
<input type="hidden" name="{{ savebutton }}" value="{{ intervalstring }}">
|
||||||
<input type="hidden" name="nrintervals" value={{ nrintervals }}>
|
<input type="hidden" name="nrintervals" value={{ nrintervals }}>
|
||||||
{% for key,value in formvalues.items %}
|
{% for key,value in formvalues.items %}
|
||||||
<input type="hidden" name="{{ key }}" value="{{ value|safe }}">
|
<input type="hidden" name="{{ key }}" value="{{ value|safe }}">
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<p>
|
<p>
|
||||||
<input type="submit" value="Save">
|
<input type="submit" value="Save">
|
||||||
</p>
|
</p>
|
||||||
<span>
|
<span>
|
||||||
<a href="">Reset to last saved</a>
|
<a href="">Reset to last saved</a>
|
||||||
|
|
||||||
<a href="/rowers/workout/{{ workout.id|encode }}/restore/">Restore Original data</a>
|
<a href="/rowers/workout/{{ workout.id|encode }}/restore/">Restore Original data</a>
|
||||||
</span>
|
</span>
|
||||||
</form>
|
</form>
|
||||||
<p>
|
<p>
|
||||||
<pre>
|
<pre>
|
||||||
{{ intervalstats }}
|
{{ intervalstats }}
|
||||||
</pre>
|
</pre>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
|
<script src="https://d3js.org/d3.v6.js"></script>
|
||||||
<script async="true" type="text/javascript">
|
|
||||||
Bokeh.set_log_level("info");
|
{{ the_div |safe }}
|
||||||
</script>
|
{{ interactiveplot |safe }}
|
||||||
|
|
||||||
{{ interactiveplot |safe }}
|
</li>
|
||||||
|
<li class="grid_2">
|
||||||
{{ the_div |safe }}
|
<h1>Feeling lucky?</h1>
|
||||||
</li>
|
<p>This new, experimental feature tries to find intervals by looking for patterns in the data. It does not
|
||||||
<li class="grid_2">
|
autodetect rest and work intervals yet. The resulting "interval string" is copied to the Interval Shorthand
|
||||||
<h1>Feeling lucky?</h1>
|
section below, where you can edit it.
|
||||||
<p>This new, experimental feature tries to find intervals by looking for patterns in the data. It does not
|
<form enctype="multipart/form-data" method="post">
|
||||||
autodetect rest and work intervals yet. The resulting "interval string" is copied to the Interval Shorthand
|
<input type="hidden" name="ruptures" value="ruptures">
|
||||||
section below, where you can edit it.
|
<table width=100%>
|
||||||
<form enctype="multipart/form-data" method="post">
|
{{ ruptureform.as_table }}
|
||||||
<input type="hidden" name="ruptures" value="ruptures">
|
</table>
|
||||||
<table width=100%>
|
{% csrf_token %}
|
||||||
{{ ruptureform.as_table }}
|
<input class="button" type="submit" value="I'm feeling lucky">
|
||||||
</table>
|
</form>
|
||||||
{% csrf_token %}
|
</p>
|
||||||
<input class="button" type="submit" value="I'm feeling lucky">
|
</li>
|
||||||
</form>
|
<li class="grid_2">
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li class="grid_2">
|
|
||||||
<h1>Interval Shorthand</h1>
|
<h1>Interval Shorthand</h1>
|
||||||
<p>
|
<p>
|
||||||
See the how-to <a href="#howto">at the bottom of this page</a> for details on how to use this form.
|
See the how-to <a href="#howto">at the bottom of this page</a> for details on how to use this form.
|
||||||
@@ -125,26 +122,26 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input class="button" type="submit" value="Update">
|
<input class="button" type="submit" value="Update">
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
<h1>Intervals by Power/Pace</h1>
|
<h1>Intervals by Power/Pace</h1>
|
||||||
|
|
||||||
<p>With this form, you can specify a power or pace level. Everything faster/harder than the
|
<p>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
|
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
|
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).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form ecntype="multipart/form-data" method="post">
|
<form ecntype="multipart/form-data" method="post">
|
||||||
<div id="slider-range"></div>
|
<div id="slider-range"></div>
|
||||||
<p>
|
<p>
|
||||||
<label for="amount">Active Range:</label>
|
<label for="amount">Active Range:</label>
|
||||||
<input type="text" id="amount" readonly style="border:0; color:#1c75bc; font-weight:bold;">
|
<input type="text" id="amount" readonly style="border:0; color:#1c75bc; font-weight:bold;">
|
||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
{{ powerupdateform.as_table }}
|
{{ powerupdateform.as_table }}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input class="button" type="submit" value="Submit">
|
<input class="button" type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
@@ -153,11 +150,11 @@
|
|||||||
<h1 id="howto">Interval Shorthand How-To</h1>
|
<h1 id="howto">Interval Shorthand How-To</h1>
|
||||||
<p>This is a quick way to enter the intervals using a special mini-language.</p>
|
<p>This is a quick way to enter the intervals using a special mini-language.</p>
|
||||||
<p>You enter something like <em>8x500m/3min</em>, 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.</p>
|
<p>You enter something like <em>8x500m/3min</em>, 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.</p>
|
||||||
|
|
||||||
<p>Special characters are <em>x</em> (times), <em>+</em> and <em>/</em> (denotes a rest interval), as well as <em>(</em> and <em>)</em>. Units are <em>min</em> (minutes), <em>sec</em> (seconds), <em>m</em> (meters) and <em>km</em> (km). </p>
|
<p>Special characters are <em>x</em> (times), <em>+</em> and <em>/</em> (denotes a rest interval), as well as <em>(</em> and <em>)</em>. Units are <em>min</em> (minutes), <em>sec</em> (seconds), <em>m</em> (meters) and <em>km</em> (km). </p>
|
||||||
|
|
||||||
<p>A typical interval is described as "<b>10min/5min</b>", with the work part before the "<b>/</b>" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "<b>1km</b>" or "<b>1000m</b>". The basic units can be combined with "<b>+</b>" and "<b>Nx</b>". You can use parentheses as in the example below.</p>
|
<p>A typical interval is described as "<b>10min/5min</b>", with the work part before the "<b>/</b>" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "<b>1km</b>" or "<b>1000m</b>". The basic units can be combined with "<b>+</b>" and "<b>Nx</b>". You can use parentheses as in the example below.</p>
|
||||||
|
|
||||||
<p>Here are a few examples</p>
|
<p>Here are a few examples</p>
|
||||||
<table class="listtable" width=100%>
|
<table class="listtable" width=100%>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -182,32 +179,32 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</li>
|
</li>
|
||||||
{% if courses %}
|
{% if courses %}
|
||||||
<li>
|
<li>
|
||||||
<h1>Interval by Course</h1>
|
<h1>Interval by Course</h1>
|
||||||
<p>
|
<p>
|
||||||
This functionality allows you to record a time on a set course that you've rowed during the workout.
|
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
|
The summary will be updated to show time on course, and you can compare this with other
|
||||||
attempts.
|
attempts.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{% if rower.share_course_results %}
|
{% if rower.share_course_results %}
|
||||||
You are currently sharing your course results with all Rowsandall users.
|
You are currently sharing your course results with all Rowsandall users.
|
||||||
Click <a href="/rowers/me/edit/?courseshare=false">here</a> to hide your course results.
|
Click <a href="/rowers/me/edit/?courseshare=false">here</a> to hide your course results.
|
||||||
{% else %}
|
{% else %}
|
||||||
You are currently hiding your course results (except for your participation in online challenges).
|
You are currently hiding your course results (except for your participation in online challenges).
|
||||||
Click <a href="/rowers/me/edit/?courseshare=true">here</a> to hide your course results.
|
Click <a href="/rowers/me/edit/?courseshare=true">here</a> to hide your course results.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<form ecntype="multipart/form-data" method="post">
|
<form ecntype="multipart/form-data" method="post">
|
||||||
<table>
|
<table>
|
||||||
{{ courseselectform.as_table }}
|
{{ courseselectform.as_table }}
|
||||||
</table>
|
</table>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input class="button" type="submit" value="Select Course">
|
<input class="button" type="submit" value="Select Course">
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -98,14 +98,11 @@
|
|||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
|
<script src="https://d3js.org/d3.v6.js"></script>
|
||||||
<script async="true" type="text/javascript">
|
|
||||||
Bokeh.set_log_level("info");
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{ interactiveplot |safe }}
|
|
||||||
|
|
||||||
{{ the_div|safe }}
|
{{ the_div|safe }}
|
||||||
|
{{ interactiveplot |safe }}
|
||||||
|
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
<li class="grid_4">
|
<li class="grid_4">
|
||||||
|
|||||||
@@ -443,9 +443,6 @@ urlpatterns = [
|
|||||||
views.trainingzones_view_data, name="trainingzones_view_data"),
|
views.trainingzones_view_data, name="trainingzones_view_data"),
|
||||||
re_path(r'^trainingzones/data/$', views.trainingzones_view_data,
|
re_path(r'^trainingzones/data/$', views.trainingzones_view_data,
|
||||||
name="trainingzones_view_data"),
|
name="trainingzones_view_data"),
|
||||||
re_path(r'^ote-bests2/user/(?P<userid>\d+)/$',
|
|
||||||
views.rankings_view2, name='rankings_view2'),
|
|
||||||
re_path(r'^ote-bests2/$', views.rankings_view2, name='rankings_view2'),
|
|
||||||
re_path(r'^analysisdata/user/(?P<userid>\d+)/$', views.analysis_view_data,
|
re_path(r'^analysisdata/user/(?P<userid>\d+)/$', views.analysis_view_data,
|
||||||
name='analysis_view_data'),
|
name='analysis_view_data'),
|
||||||
re_path(r'^analysisdata/$', views.analysis_view_data,
|
re_path(r'^analysisdata/$', views.analysis_view_data,
|
||||||
@@ -470,8 +467,6 @@ urlpatterns = [
|
|||||||
views.workout_upload_view, name='workout_upload_view'),
|
views.workout_upload_view, name='workout_upload_view'),
|
||||||
re_path(r'^workout/upload/$', views.workout_upload_view,
|
re_path(r'^workout/upload/$', views.workout_upload_view,
|
||||||
name='workout_upload_view'),
|
name='workout_upload_view'),
|
||||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/histo/$', views.workout_histo_view,
|
|
||||||
name='workout_histo_view'),
|
|
||||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/forcecurve/$', views.workout_forcecurve_view,
|
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/forcecurve/$', views.workout_forcecurve_view,
|
||||||
name='workout_forcecurve_view'),
|
name='workout_forcecurve_view'),
|
||||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/forcecurve/(?P<analysis>\d+)/$', views.workout_forcecurve_view,
|
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/forcecurve/(?P<analysis>\d+)/$', views.workout_forcecurve_view,
|
||||||
|
|||||||
@@ -1382,398 +1382,6 @@ def ajax_agegrouprecords(request,
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Show ranking distances including predicted paces
|
|
||||||
@login_required()
|
|
||||||
@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True)
|
|
||||||
def rankings_view2(request, userid=0,
|
|
||||||
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
||||||
enddate=timezone.now(),
|
|
||||||
deltadays=-1,
|
|
||||||
startdatestring="",
|
|
||||||
enddatestring=""):
|
|
||||||
|
|
||||||
if deltadays > 0: # pragma: no cover
|
|
||||||
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
||||||
|
|
||||||
if startdatestring != "": # pragma: no cover
|
|
||||||
startdate = iso8601.parse_date(startdatestring)
|
|
||||||
|
|
||||||
if enddatestring != "": # pragma: no cover
|
|
||||||
enddate = iso8601.parse_date(enddatestring)
|
|
||||||
|
|
||||||
if enddate < startdate: # pragma: no cover
|
|
||||||
s = enddate
|
|
||||||
enddate = startdate
|
|
||||||
startdate = s
|
|
||||||
|
|
||||||
if userid == 0:
|
|
||||||
userid = request.user.id
|
|
||||||
else:
|
|
||||||
lastupdated = "1900-01-01"
|
|
||||||
|
|
||||||
promember = 0
|
|
||||||
r = getrequestrower(request, userid=userid)
|
|
||||||
theuser = r.user
|
|
||||||
|
|
||||||
wcdurations = []
|
|
||||||
wcpower = []
|
|
||||||
|
|
||||||
lastupdated = "1900-01-01"
|
|
||||||
userid = 0
|
|
||||||
if 'options' in request.session:
|
|
||||||
options = request.session['options']
|
|
||||||
try:
|
|
||||||
wcdurations = options['wcdurations']
|
|
||||||
wcpower = options['wcpower']
|
|
||||||
lastupdated = options['lastupdated']
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
userid = options['userid']
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
userid = 0
|
|
||||||
else:
|
|
||||||
options = {}
|
|
||||||
|
|
||||||
lastupdatedtime = arrow.get(lastupdated).timestamp()
|
|
||||||
current_time = arrow.utcnow().timestamp()
|
|
||||||
|
|
||||||
deltatime_seconds = current_time - lastupdatedtime
|
|
||||||
recalc = False
|
|
||||||
if str(userid) != str(theuser) or deltatime_seconds > 3600:
|
|
||||||
recalc = True
|
|
||||||
options['lastupdated'] = arrow.utcnow().isoformat()
|
|
||||||
else: # pragma: no cover
|
|
||||||
recalc = False
|
|
||||||
|
|
||||||
options['userid'] = theuser.id
|
|
||||||
|
|
||||||
if r.birthdate:
|
|
||||||
age = calculate_age(r.birthdate)
|
|
||||||
else:
|
|
||||||
age = 0
|
|
||||||
|
|
||||||
agerecords = CalcAgePerformance.objects.filter(
|
|
||||||
age=age,
|
|
||||||
sex=r.sex,
|
|
||||||
weightcategory=r.weightcategory)
|
|
||||||
|
|
||||||
if len(agerecords) == 0:
|
|
||||||
recalc = True
|
|
||||||
wcpower = []
|
|
||||||
wcdurations = []
|
|
||||||
else:
|
|
||||||
wcdurations = []
|
|
||||||
wcpower = []
|
|
||||||
for record in agerecords:
|
|
||||||
wcdurations.append(record.duration)
|
|
||||||
wcpower.append(record.power)
|
|
||||||
|
|
||||||
options['wcpower'] = wcpower
|
|
||||||
options['wcdurations'] = wcdurations
|
|
||||||
if theuser:
|
|
||||||
options['userid'] = theuser.id
|
|
||||||
|
|
||||||
request.session['options'] = options
|
|
||||||
|
|
||||||
result = request.user.is_authenticated and ispromember(request.user)
|
|
||||||
if result:
|
|
||||||
promember = 1
|
|
||||||
|
|
||||||
# get all indoor rows in date range
|
|
||||||
|
|
||||||
# process form
|
|
||||||
if request.method == 'POST' and "daterange" in request.POST:
|
|
||||||
dateform = DateRangeForm(request.POST)
|
|
||||||
deltaform = DeltaDaysForm(request.POST)
|
|
||||||
if dateform.is_valid():
|
|
||||||
startdate = dateform.cleaned_data['startdate']
|
|
||||||
enddate = dateform.cleaned_data['enddate']
|
|
||||||
if startdate > enddate: # pragma: no cover
|
|
||||||
s = enddate
|
|
||||||
enddate = startdate
|
|
||||||
startdate = s
|
|
||||||
elif request.method == 'POST' and "datedelta" in request.POST: # pragma: no cover
|
|
||||||
deltaform = DeltaDaysForm(request.POST)
|
|
||||||
if deltaform.is_valid():
|
|
||||||
deltadays = deltaform.cleaned_data['deltadays']
|
|
||||||
if deltadays:
|
|
||||||
enddate = timezone.now()
|
|
||||||
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
||||||
if startdate > enddate:
|
|
||||||
s = enddate
|
|
||||||
enddate = startdate
|
|
||||||
startdate = s
|
|
||||||
dateform = DateRangeForm(initial={
|
|
||||||
'startdate': startdate,
|
|
||||||
'enddate': enddate,
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
dateform = DateRangeForm()
|
|
||||||
deltaform = DeltaDaysForm()
|
|
||||||
|
|
||||||
else:
|
|
||||||
dateform = DateRangeForm(initial={
|
|
||||||
'startdate': startdate,
|
|
||||||
'enddate': enddate,
|
|
||||||
})
|
|
||||||
deltaform = DeltaDaysForm()
|
|
||||||
|
|
||||||
# get all 2k (if any) - this rower, in date range
|
|
||||||
try:
|
|
||||||
r = getrower(theuser)
|
|
||||||
except Rower.DoesNotExist: # pragma: no cover
|
|
||||||
r = 0
|
|
||||||
|
|
||||||
uu = theuser
|
|
||||||
|
|
||||||
# test to fix bug
|
|
||||||
startdate = datetime.datetime.combine(startdate, datetime.time())
|
|
||||||
enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59))
|
|
||||||
startdate = arrow.get(startdate).datetime
|
|
||||||
enddate = arrow.get(enddate).datetime
|
|
||||||
|
|
||||||
thedistances = []
|
|
||||||
theworkouts = []
|
|
||||||
thesecs = []
|
|
||||||
|
|
||||||
rankingdistances.sort()
|
|
||||||
rankingdurations.sort()
|
|
||||||
|
|
||||||
for rankingdistance in rankingdistances:
|
|
||||||
|
|
||||||
workouts = Workout.objects.filter(
|
|
||||||
user=r, distance=rankingdistance,
|
|
||||||
workouttype__in=['rower', 'dynamic', 'slides'],
|
|
||||||
rankingpiece=True,
|
|
||||||
startdatetime__gte=startdate,
|
|
||||||
startdatetime__lte=enddate).order_by('duration')
|
|
||||||
if workouts:
|
|
||||||
thedistances.append(rankingdistance)
|
|
||||||
theworkouts.append(workouts[0])
|
|
||||||
|
|
||||||
timesecs = 3600*workouts[0].duration.hour
|
|
||||||
timesecs += 60*workouts[0].duration.minute
|
|
||||||
timesecs += workouts[0].duration.second
|
|
||||||
timesecs += 1.e-6*workouts[0].duration.microsecond
|
|
||||||
|
|
||||||
thesecs.append(timesecs)
|
|
||||||
|
|
||||||
for rankingduration in rankingdurations:
|
|
||||||
|
|
||||||
workouts = Workout.objects.filter(
|
|
||||||
user=r, duration=rankingduration,
|
|
||||||
workouttype='rower',
|
|
||||||
rankingpiece=True,
|
|
||||||
startdatetime__gte=startdate,
|
|
||||||
startdatetime__lte=enddate).order_by('-distance')
|
|
||||||
if workouts:
|
|
||||||
thedistances.append(workouts[0].distance)
|
|
||||||
theworkouts.append(workouts[0])
|
|
||||||
|
|
||||||
timesecs = 3600*workouts[0].duration.hour
|
|
||||||
timesecs += 60*workouts[0].duration.minute
|
|
||||||
timesecs += workouts[0].duration.second
|
|
||||||
timesecs += 1.e-5*workouts[0].duration.microsecond
|
|
||||||
|
|
||||||
thesecs.append(timesecs)
|
|
||||||
|
|
||||||
thedistances = np.array(thedistances)
|
|
||||||
thesecs = np.array(thesecs)
|
|
||||||
|
|
||||||
thevelos = thedistances/thesecs
|
|
||||||
theavpower = 2.8*(thevelos**3)
|
|
||||||
|
|
||||||
# create interactive plot
|
|
||||||
if len(thedistances) != 0:
|
|
||||||
res = interactive_cpchart(
|
|
||||||
r, thedistances, thesecs, theavpower,
|
|
||||||
theworkouts, promember=promember,
|
|
||||||
wcdurations=wcdurations, wcpower=wcpower
|
|
||||||
)
|
|
||||||
script = res[0]
|
|
||||||
div = res[1]
|
|
||||||
paulslope = res[2]
|
|
||||||
paulintercept = res[3]
|
|
||||||
p1 = res[4]
|
|
||||||
message = res[5]
|
|
||||||
else:
|
|
||||||
script = ''
|
|
||||||
div = '<p>No ranking pieces found.</p>'
|
|
||||||
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()
|
@login_required()
|
||||||
def otecp_toadmin_view(request, theuser=0,
|
def otecp_toadmin_view(request, theuser=0,
|
||||||
startdate=timezone.now() - datetime.timedelta(days=365),
|
startdate=timezone.now() - datetime.timedelta(days=365),
|
||||||
|
|||||||
@@ -630,50 +630,6 @@ def otw_use_gps(request, id=0):
|
|||||||
return HttpResponseRedirect(url)
|
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
|
# add a workout manually
|
||||||
@login_required()
|
@login_required()
|
||||||
|
|||||||
Reference in New Issue
Block a user