diff --git a/rowers/models.py b/rowers/models.py index 78b95e1b..1cc2fcfb 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -363,22 +363,22 @@ class StrokeData(models.Model): index_together = ['workoutid'] workoutid = models.IntegerField(null=True) - time = models.FloatField(null=True) - hr = models.IntegerField(null=True) - pace = models.FloatField(null=True) + time = models.FloatField(null=True,verbose_name='Time') + hr = models.IntegerField(null=True,verbose_name='Heart Rate') + pace = models.FloatField(null=True,verbose_name='Pace') workoutstate = models.IntegerField(null=True,default=1) - spm = models.FloatField(null=True) - cumdist = models.FloatField(null=True) + spm = models.FloatField(null=True,verbose_name='Stroke Rate') + cumdist = models.FloatField(null=True,verbose_name='Cumulative Distance') ftime = models.CharField(max_length=30) fpace = models.CharField(max_length=30) - driveenergy = models.FloatField(null=True) - power = models.FloatField(null=True) - averageforce = models.FloatField(null=True) - drivelength = models.FloatField(null=True) - peakforce = models.FloatField(null=True) - forceratio = models.FloatField(null=True) - distance = models.FloatField(null=True) - drivespeed = models.FloatField(null=True) + driveenergy = models.FloatField(null=True,verbose_name='Work per Stroke') + power = models.FloatField(null=True,verbose_name='Power') + averageforce = models.FloatField(null=True,verbose_name='Average Force') + drivelength = models.FloatField(null=True,verbose_name='Drive Length') + peakforce = models.FloatField(null=True,verbose_name='Peak Force') + forceratio = models.FloatField(null=True,verbose_name='Average/Peak Force Ratio') + distance = models.FloatField(null=True,verbose_name='Distance') + drivespeed = models.FloatField(null=True,verbose_name='Drive Speed') hr_ut2 = models.IntegerField(null=True) hr_ut1 = models.IntegerField(null=True) hr_at = models.IntegerField(null=True) @@ -392,12 +392,12 @@ class StrokeData(models.Model): equivergpower = models.FloatField(null=True) fergpace = models.CharField(max_length=30) fnowindpace = models.CharField(max_length=30) - catch = models.FloatField(default=0,null=True) - slip = models.FloatField(default=0,null=True) - finish = models.FloatField(default=0,null=True) - wash = models.FloatField(default=0,null=True) - peakforceangle = models.FloatField(default=0,null=True) - rhythm = models.FloatField(default=1.0,null=True) + catch = models.FloatField(default=0,null=True,verbose_name='Catch Angle') + slip = models.FloatField(default=0,null=True,verbose_name='Slip') + finish = models.FloatField(default=0,null=True,verbose_name='Finish Angle') + wash = models.FloatField(default=0,null=True,verbose_name='Wash') + peakforceangle = models.FloatField(default=0,null=True,verbose_name='Peak Force Angle') + rhythm = models.FloatField(default=1.0,null=True,verbose_name='Rhythm') # A wrapper around the png files class GraphImage(models.Model): diff --git a/rowers/templates/workoutstats.html b/rowers/templates/workoutstats.html index aebd71a4..3f470672 100644 --- a/rowers/templates/workoutstats.html +++ b/rowers/templates/workoutstats.html @@ -7,6 +7,10 @@ {% block content %}

Workout Statistics for {{ workout.name }}

+

+ This is an experimental page which just lists a bunch of statistics for + your workout. This page is under rapid development. +

Edit Workout @@ -37,10 +41,10 @@ If your data source allows, this will show or hide strokes taken during rest intervals.

-
+
{% if stats %} {% for key, value in stats.items %} -

{{ key }}

+

{{ value.verbosename }}

@@ -69,5 +73,45 @@ {% endfor %} {% endif %} +
+ {% if cordict %} +

Correlation table

+

This table indicates a positive (+) or negative (-) correlation between two parameters. The strong correlations are indicated with ++ and --. +

+
+ + + + {% for key,value in cordict.items %} + + {% endfor %} + + + + {% for key, thedict in cordict.items %} + + + {% for key2,value in thedict.items %} + + {% endfor %} + + {% endfor %} + +
 
{{ key }}
{{ key }} + {% if value > 0.5 %} +
++
+ {% elif value > 0.1 %} +
+
+ {% elif value < -0.5 %} +
--
+ {% elif value < -0.1 %} +
-
+ {% else %} +   + {% endif %} +
+ + {% endif %} +
{% endblock %} diff --git a/rowers/views.py b/rowers/views.py index e6a1b021..779b1b89 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -2638,30 +2638,36 @@ def workout_stats_view(request,id=0,message="",successmessage=""): 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') - fieldnames.remove('cumdist') + fields = StrokeData._meta.get_fields() - for field in fieldnames: + 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(): thedict = { 'mean':datadf[field].mean(), 'min': datadf[field].min(), @@ -2670,9 +2676,23 @@ def workout_stats_view(request,id=0,message="",successmessage=""): 'median': datadf[field].median(), 'firstq':datadf[field].quantile(q=0.25), 'thirdq':datadf[field].quantile(q=0.75), + 'verbosename':verbosename, } 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 return render(request, 'workoutstats.html', @@ -2680,6 +2700,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): 'stats':stats, 'workout':row, 'workstrokesonly':workstrokesonly, + 'cordict':cordict, }) # The Advanced edit page diff --git a/static/css/rowsandall.css b/static/css/rowsandall.css index a3f4e761..6aa18088 100644 --- a/static/css/rowsandall.css +++ b/static/css/rowsandall.css @@ -60,6 +60,33 @@ th { } .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 { table-layout: fixed; @@ -76,6 +103,22 @@ th { .midden { text-align: center } +.poscor { + background-color: #8f8; +} + +.weakposcor { + background-color: #efe; +} + +.negcor { + background-color:#f88; +} + +.weaknegcor { + background-color: #fee; +} + .successmessage { border: 1px solid #000; background-color: #8f8;