Stats page v2
This commit is contained in:
@@ -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):
|
||||||
|
|||||||
@@ -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> </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 %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -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,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user