Private
Public Access
1
0

Stats page v2

This commit is contained in:
Sander Roosendaal
2017-02-06 15:07:24 +01:00
parent 38f7a59476
commit fb76710681
4 changed files with 153 additions and 45 deletions

View File

@@ -363,22 +363,22 @@ class StrokeData(models.Model):
index_together = ['workoutid'] index_together = ['workoutid']
workoutid = models.IntegerField(null=True) workoutid = models.IntegerField(null=True)
time = models.FloatField(null=True) time = models.FloatField(null=True,verbose_name='Time')
hr = models.IntegerField(null=True) hr = models.IntegerField(null=True,verbose_name='Heart Rate')
pace = models.FloatField(null=True) pace = models.FloatField(null=True,verbose_name='Pace')
workoutstate = models.IntegerField(null=True,default=1) workoutstate = models.IntegerField(null=True,default=1)
spm = models.FloatField(null=True) spm = models.FloatField(null=True,verbose_name='Stroke Rate')
cumdist = models.FloatField(null=True) cumdist = models.FloatField(null=True,verbose_name='Cumulative Distance')
ftime = models.CharField(max_length=30) ftime = models.CharField(max_length=30)
fpace = models.CharField(max_length=30) fpace = models.CharField(max_length=30)
driveenergy = models.FloatField(null=True) driveenergy = models.FloatField(null=True,verbose_name='Work per Stroke')
power = models.FloatField(null=True) power = models.FloatField(null=True,verbose_name='Power')
averageforce = models.FloatField(null=True) averageforce = models.FloatField(null=True,verbose_name='Average Force')
drivelength = models.FloatField(null=True) drivelength = models.FloatField(null=True,verbose_name='Drive Length')
peakforce = models.FloatField(null=True) peakforce = models.FloatField(null=True,verbose_name='Peak Force')
forceratio = models.FloatField(null=True) forceratio = models.FloatField(null=True,verbose_name='Average/Peak Force Ratio')
distance = models.FloatField(null=True) distance = models.FloatField(null=True,verbose_name='Distance')
drivespeed = models.FloatField(null=True) drivespeed = models.FloatField(null=True,verbose_name='Drive Speed')
hr_ut2 = models.IntegerField(null=True) hr_ut2 = models.IntegerField(null=True)
hr_ut1 = models.IntegerField(null=True) hr_ut1 = models.IntegerField(null=True)
hr_at = models.IntegerField(null=True) hr_at = models.IntegerField(null=True)
@@ -392,12 +392,12 @@ class StrokeData(models.Model):
equivergpower = models.FloatField(null=True) equivergpower = models.FloatField(null=True)
fergpace = models.CharField(max_length=30) fergpace = models.CharField(max_length=30)
fnowindpace = models.CharField(max_length=30) fnowindpace = models.CharField(max_length=30)
catch = models.FloatField(default=0,null=True) catch = models.FloatField(default=0,null=True,verbose_name='Catch Angle')
slip = models.FloatField(default=0,null=True) slip = models.FloatField(default=0,null=True,verbose_name='Slip')
finish = models.FloatField(default=0,null=True) finish = models.FloatField(default=0,null=True,verbose_name='Finish Angle')
wash = models.FloatField(default=0,null=True) wash = models.FloatField(default=0,null=True,verbose_name='Wash')
peakforceangle = models.FloatField(default=0,null=True) peakforceangle = models.FloatField(default=0,null=True,verbose_name='Peak Force Angle')
rhythm = models.FloatField(default=1.0,null=True) rhythm = models.FloatField(default=1.0,null=True,verbose_name='Rhythm')
# A wrapper around the png files # A wrapper around the png files
class GraphImage(models.Model): class GraphImage(models.Model):

View File

@@ -7,6 +7,10 @@
{% block content %} {% block content %}
<div class="grid_12 alpha"> <div class="grid_12 alpha">
<h1>Workout Statistics for {{ workout.name }}</h1> <h1>Workout Statistics for {{ workout.name }}</h1>
<p>
This is an experimental page which just lists a bunch of statistics for
your workout. This page is under rapid development.
</p>
<div class="grid_2 alpha"> <div class="grid_2 alpha">
<p> <p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a> <a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
@@ -37,10 +41,10 @@
<span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span> <span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span>
</div> </div>
</div> </div>
<div class="grid_6 alpha"> <div class="grid_4 alpha">
{% if stats %} {% if stats %}
{% for key, value in stats.items %} {% for key, value in stats.items %}
<h2>{{ key }}</h2> <h2>{{ value.verbosename }}</h2>
<table width="100%" class="listtable"> <table width="100%" class="listtable">
<thead> <thead>
<tr> <tr>
@@ -69,5 +73,45 @@
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</div> </div>
<div class="grid_8 omega">
{% if cordict %}
<h2> Correlation table</h2>
<p>This table indicates a positive (+) or negative (-) correlation between two parameters. The strong correlations are indicated with ++ and --.
</p>
<table width="90%" class="cortable">
<thead>
<tr>
<th>&nbsp;</th>
{% for key,value in cordict.items %}
<th class="rotate"><div><span>{{ key }}</span></div></th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for key, thedict in cordict.items %}
<tr>
<th> {{ key }}</th>
{% for key2,value in thedict.items %}
<td>
{% if value > 0.5 %}
<div class="poscor">++</div>
{% elif value > 0.1 %}
<div class="weakposcor">+</div>
{% elif value < -0.5 %}
<div class="negcor">--</div>
{% elif value < -0.1 %}
<div class="weaknegcor">-</div>
{% else %}
&nbsp;
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
{% endblock %} {% endblock %}

View File

@@ -2638,30 +2638,36 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
# Create stats # Create stats
stats = {} stats = {}
# Get field names and remove those that are not useful in stats # Get field names and remove those that are not useful in stats
fieldnames = StrokeData._meta.get_all_field_names()
fieldnames.remove('workoutid')
fieldnames.remove('ergpace')
fieldnames.remove('hr_an')
fieldnames.remove('hr_tr')
fieldnames.remove('hr_at')
fieldnames.remove('hr_ut2')
fieldnames.remove('hr_ut1')
fieldnames.remove('time')
fieldnames.remove('distance')
fieldnames.remove('nowindpace')
fieldnames.remove('fnowindpace')
fieldnames.remove('fergpace')
fieldnames.remove('equivergpower')
fieldnames.remove('workoutstate')
fieldnames.remove('fpace')
fieldnames.remove('id')
fieldnames.remove('ftime')
fieldnames.remove('x_right')
fieldnames.remove('hr_max')
fieldnames.remove('hr_bottom')
fields = StrokeData._meta.get_fields() fields = StrokeData._meta.get_fields()
fielddict = {field.name:field.verbose_name for field in fields}
print fielddict
fielddict.pop('workoutid')
fielddict.pop('ergpace')
fielddict.pop('hr_an')
fielddict.pop('hr_tr')
fielddict.pop('hr_at')
fielddict.pop('hr_ut2')
fielddict.pop('hr_ut1')
fielddict.pop('time')
fielddict.pop('distance')
fielddict.pop('nowindpace')
fielddict.pop('fnowindpace')
fielddict.pop('fergpace')
fielddict.pop('equivergpower')
fielddict.pop('workoutstate')
fielddict.pop('fpace')
fielddict.pop('pace')
fielddict.pop('id')
fielddict.pop('ftime')
fielddict.pop('x_right')
fielddict.pop('hr_max')
fielddict.pop('hr_bottom')
fielddict.pop('cumdist')
for field,verbosename in fielddict.iteritems(): for field,verbosename in fielddict.iteritems():
thedict = { thedict = {
'mean':datadf[field].mean(), 'mean':datadf[field].mean(),
@@ -2670,9 +2676,23 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
'max': datadf[field].max(), 'max': datadf[field].max(),
'median': datadf[field].median(), 'median': datadf[field].median(),
'firstq':datadf[field].quantile(q=0.25), 'firstq':datadf[field].quantile(q=0.25),
'thirdq':datadf[field].quantile(q=0.75),
'verbosename':verbosename, 'verbosename':verbosename,
} }
stats[field] = thedict stats[field] = thedict
# Create a dict with correlation values
cor = datadf.corr()
cor.fillna(value=0,inplace=True)
cordict = {}
for field1,verbosename in fielddict.iteritems():
thedict = {}
for field2,verbosename in fielddict.iteritems():
try:
thedict[field2] = cor.ix[field1,field2]
except KeyError:
thedict[field2] = 0
cordict[field1] = thedict cordict[field1] = thedict
return render(request, return render(request,
@@ -2680,6 +2700,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
{ {
'stats':stats, 'stats':stats,
'workout':row, 'workout':row,
'workstrokesonly':workstrokesonly,
'cordict':cordict, 'cordict':cordict,
}) })

View File

@@ -60,6 +60,33 @@ th {
} }
.paddedtable td { padding: 1px 20px } .paddedtable td { padding: 1px 20px }
.cortable {
border-collapse: collapse;
}
.cortable td {
border: 1px solid #999;
text-align: center;
}
th.rotate {
/* Something you can count on */
height: 78px;
white-space: nowrap;
}
th.rotate > div {
transform:
/* Magic Numbers */
translate(18px, 51px)
/* 45 is really 360 - 45 */
rotate(315deg);
width: 30px;
}
th.rotate > div > span {
border-bottom: 1px solid #ccc;
padding: 5px 5px;
}
.fixtable table { .fixtable table {
table-layout: fixed; table-layout: fixed;
@@ -76,6 +103,22 @@ th {
.midden { text-align: center } .midden { text-align: center }
.poscor {
background-color: #8f8;
}
.weakposcor {
background-color: #efe;
}
.negcor {
background-color:#f88;
}
.weaknegcor {
background-color: #fee;
}
.successmessage { .successmessage {
border: 1px solid #000; border: 1px solid #000;
background-color: #8f8; background-color: #8f8;