diff --git a/rowers/alerts.py b/rowers/alerts.py
index f33bd3f9..6e9fcd12 100644
--- a/rowers/alerts.py
+++ b/rowers/alerts.py
@@ -169,6 +169,10 @@ def alert_get_stats(alert,nperiod=0):
percentage = int(100.*nr_strokes_qualifying/nr_strokes)
else:
percentage = 0
+
+ median_q = df2[alert.measured.metric].median()
+ median = df[alert.measured.metric].median()
+ std = df[alert.measured.metric].std()
return {
'workouts':len(workouts),
@@ -177,7 +181,10 @@ def alert_get_stats(alert,nperiod=0):
'nr_strokes':nr_strokes,
'nr_strokes_qualifying':nr_strokes_qualifying,
'percentage': percentage,
- 'nperiod':nperiod,
+ 'nperiod':nperiod,
+ 'median':median,
+ 'median_q':median_q,
+ 'standard_dev':std,
}
# run alert report
diff --git a/rowers/models.py b/rowers/models.py
index f9d2ae9f..3028d064 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -881,9 +881,12 @@ class Rower(models.Model):
def save(self, *args, **kwargs):
try:
for group in self.coachinggroups.all():
- coach = Rower.objects.get(mycoachgroup=group)
- if coach.rowerplan == 'freecoach':
- self.coachinggroups.remove(group)
+ try:
+ coach = Rower.objects.get(mycoachgroup=group)
+ if coach.rowerplan == 'freecoach':
+ self.coachinggroups.remove(group)
+ except Rower.DoesNotExist:
+ pass
except ValueError:
pass
@@ -1093,6 +1096,11 @@ class Alert(models.Model):
return stri
+ def metricname(self):
+ metricdict = {key:value for (key,value) in parchoicesy1}
+
+ return metricdict[self.measured.metric]
+
def description(self):
metricdict = {key:value for (key,value) in parchoicesy1}
@@ -1115,6 +1123,24 @@ class Alert(models.Model):
return description
+ def shortdescription(self):
+ metricdict = {key:value for (key,value) in parchoicesy1}
+
+ if self.measured.condition == 'between':
+ description = '{value1} < {metric} < {value2}'.format(
+ metric = self.measured.metric,
+ value1 = self.measured.value1,
+ value2 = self.measured.value2,
+ )
+ else:
+ description = '{metric} {condition} {value1}'.format(
+ metric = self.measured.metric,
+ value1 = self.measured.value1,
+ condition = self.measured.condition
+ )
+
+ return description
+
class AlertEditForm(ModelForm):
class Meta:
diff --git a/rowers/tasks.py b/rowers/tasks.py
index 3aab4fbe..cf0bae49 100644
--- a/rowers/tasks.py
+++ b/rowers/tasks.py
@@ -756,6 +756,26 @@ def handle_updatedps(useremail, workoutids, debug=False,**kwargs):
return 1
+import math
+def sigdig(value, digits = 3):
+ try:
+ order = int(math.floor(math.log10(math.fabs(value))))
+ except (ValueError,TypeError):
+ return value
+
+ # return integers as is
+ if value % 1 == 0:
+ return value
+
+ places = digits - order - 1
+ if places > 0:
+ fmtstr = "%%.%df" % (places)
+ else:
+ fmtstr = "%.0f"
+ return fmtstr % (round(value, places))
+
+
+
@app.task
def handle_send_email_alert(
useremail, userfirstname, userlastname, rowerfirstname, alertname, stats, **kwargs):
@@ -770,15 +790,26 @@ def handle_send_email_alert(
else:
othertexts = None
- subject = "Your rowing performance on rowsandall.com ({startdate} to {enddate})".format(
+ report = {}
+ report['Percentage'] = int(stats['percentage'])
+ report['Number of workouts'] = int(stats['workouts'])
+ report['Data set'] = "{a} strokes out of {b}".format(
+ a = stats['nr_strokes_qualifying'],
+ b = stats['nr_strokes']
+ )
+ report['Median'] = sigdig(stats['median'])
+ report['Median of qualifying strokes'] = sigdig(stats['median_q'])
+
+ subject = "Rowsandall.com: {alertname} ({startdate} to {enddate})".format(
startdate = stats['startdate'],
enddate = stats['enddate'],
+ alertname=alertname,
)
from_email = 'Rowsandall
{{ alert }}
{{ alert.description }}
This is a page under construction. Currently with minimal information
{{ value }}
+{{ stats|lookup:'workouts' }} workouts
+{{ stats|lookup:'nr_strokes_qualifying' }} strokes out of {{ stats|lookup:'nr_strokes' }}
+Median {{ alert.metricname }}: {{ stats|lookup:'median'|sigdig }}
+Median {{ alert.metricname }}: {{ stats|lookup:'median_q'|sigdig }} ({{ alert.shortdescription }})
-
- {% block body %}
-
- {% endblock %}
-
+
+
+ {% block body %}
+
+ {% endblock %}
+
diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py
index e3ee8050..d7f9dd4a 100644
--- a/rowers/templatetags/rowerfilters.py
+++ b/rowers/templatetags/rowerfilters.py
@@ -4,6 +4,7 @@ from time import strftime
from django.utils import timezone
import dateutil.parser
import json
+import math
import datetime
import re
register = template.Library()
@@ -37,6 +38,25 @@ from django.template.defaultfilters import stringfilter
from six import string_types
+@register.filter
+def sigdig(value, digits = 3):
+ try:
+ order = int(math.floor(math.log10(math.fabs(value))))
+ except (ValueError,TypeError):
+ return value
+
+ # return integers as is
+ if value % 1 == 0:
+ return value
+
+ places = digits - order - 1
+ if places > 0:
+ fmtstr = "%%.%df" % (places)
+ else:
+ fmtstr = "%.0f"
+ return fmtstr % (round(value, places))
+
+
@register.filter(is_safe=True, needs_autoescape=True)
@stringfilter
def urlshorten(value, limit,autoescape=None):
@@ -278,18 +298,26 @@ def jsdict(dict,key):
s = dict.get(key)
return mark_safe(json.dumps(s))
+
+
@register.filter
def lookup(dict, key):
- s = dict.get(key)
-
+ try:
+ s = dict.get(key)
+ except KeyError:
+ return None
+
if isinstance(s,string_types) and len(s) > 22:
s = s[:22]
return s
@register.filter
def lookuplong(dict, key):
- s = dict.get(key)
-
+ try:
+ s = dict.get(key)
+ except KeyError:
+ return None
+
return s
@register.filter
diff --git a/rowers/tests/testdata/testdata.csv.gz b/rowers/tests/testdata/testdata.csv.gz
index 9941ccb4..784c98fe 100644
Binary files a/rowers/tests/testdata/testdata.csv.gz and b/rowers/tests/testdata/testdata.csv.gz differ
diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py
index c8d68209..6bb2801c 100644
--- a/rowers/views/analysisviews.py
+++ b/rowers/views/analysisviews.py
@@ -4328,7 +4328,8 @@ def alerts_view(request,userid=0):
for alert in alerts:
stats.append(alert_get_stats(alert))
-
+
+
breadcrumbs = [
{
'url':'/rowers/analysis',