diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py
index 035946cc..7aee9332 100644
--- a/rowers/views/analysisviews.py
+++ b/rowers/views/analysisviews.py
@@ -4324,8 +4324,20 @@ def alerts_view(request,userid=0):
alerts = Alert.objects.filter(rower=r).order_by('next_run')
+ breadcrumbs = [
+ {
+ 'url':'/rowers/analysis',
+ 'name': 'Analysis'
+ },
+ {
+ 'url': reverse('alerts_view'),
+ 'name': 'Alerts',
+ },
+ ]
+
return render(request,'alerts.html',
{
+ 'breadcrumbs':breadcrumbs,
'alerts':alerts,
'rower':r,
})
@@ -4337,12 +4349,51 @@ def alerts_view(request,userid=0):
def alert_create_view(request,userid=0):
r = getrequestrower(request,userid=userid)
- form = AlertEditForm()
+ if request.method == 'POST':
+ form = AlertEditForm(request.POST)
+ measuredform = ConditionEditForm(request.POST)
+ if form.is_valid() and measuredform.is_valid():
+ ad = form.cleaned_data
+ measured = measuredform.cleaned_data
+
+ period = ad['period']
+ emailalert = ad['emailalert']
+ reststrokes = ad['reststrokes']
+ workouttype = ad['workouttype']
+ name = ad['name']
+
+ result = create_alert(request.user,r,measured,period=period,emailalert=emailalert,
+ reststrokes=reststrokes,workouttype=workouttype,
+ name=name)
+
+ if result:
+ url = reverse('alert_edit_view',kwargs={'id':result})
+ return HttpResponseRedirect(url)
+ else:
+ form = AlertEditForm()
+ measuredform = ConditionEditForm()
+ breadcrumbs = [
+ {
+ 'url':'/rowers/analysis',
+ 'name': 'Analysis'
+ },
+ {
+ 'url': reverse('alerts_view'),
+ 'name': 'Alerts',
+ },
+ {
+ 'url': reverse('alert_create_view'),
+ 'name': 'Create'
+ }
+ ]
+
return render(request,'alert_create.html',
{
+ 'breadcrumbs':breadcrumbs,
'rower':r,
'form':form,
+ 'measuredform':measuredform,
})
# alert report view
@@ -4354,12 +4405,35 @@ def alert_create_view(request,userid=0):
def alert_edit_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
- form = AlertEditForm()
+ alert = Alert.objects.get(id=id)
+
+
+ form = AlertEditForm(instance=alert)
+ measuredform = ConditionEditForm(instance=alert.measured)
+
+ breadcrumbs = [
+ {
+ 'url':'/rowers/analysis',
+ 'name': 'Analysis'
+ },
+ {
+ 'url':reverse('alerts_view'),
+ 'name':'Alerts',
+ },
+ {
+ 'url': reverse('alert_edit_view',
+ kwargs={'userid':userid,'id':alert.id}),
+ 'name': alert.name,
+ },
+ ]
+
return render(request,'alert_edit.html',
{
+ 'breadcrumbs':breadcrumbs,
'rower':r,
'form':form,
+ 'measuredform':measuredform,
})
# alert delete view
@@ -4385,6 +4459,10 @@ class AlertDelete(DeleteView):
'url':'/rowers/analysis',
'name': 'Analysis'
},
+ {
+ 'url':reverse('alerts_view'),
+ 'name':'Alerts',
+ },
{
'url': reverse('alert_edit_view',
kwargs={'userid':userid,'id':self.object.pk}),
diff --git a/rowers/views/statements.py b/rowers/views/statements.py
index 2f62d5c1..5392e757 100644
--- a/rowers/views/statements.py
+++ b/rowers/views/statements.py
@@ -94,7 +94,7 @@ from rowers.models import (
microcyclecheckdates,mesocyclecheckdates,macrocyclecheckdates,
TrainingMesoCycleForm, TrainingMicroCycleForm,
RaceLogo,RowerBillingAddressForm,PaidPlan,
- AlertEditForm,
+ AlertEditForm, ConditionEditForm,
PlannedSessionComment,CoachRequest,CoachOffer,checkaccessplanuser
)
from rowers.models import (
@@ -208,6 +208,7 @@ import numpy as np
import matplotlib.pyplot as plt
from rowers.emails import send_template_email,htmlstrip
+from rowers.alerts import *
from pytz import timezone as tz,utc
from timezonefinder import TimezoneFinder
From 8009831ab14e0bba5f39d9054abfa7e365ca5d45 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Sat, 17 Aug 2019 15:17:11 +0200
Subject: [PATCH 08/13] alert create including filters
---
rowers/alerts.py | 9 +++--
rowers/models.py | 15 +++++++-
rowers/templates/.#laboratory.html | 1 -
rowers/templates/alert_create.html | 59 +++++++++++++++++++++++++-----
rowers/templates/laboratory.html | 4 +-
rowers/views/analysisviews.py | 34 +++++++++++++++--
rowers/views/statements.py | 2 +-
7 files changed, 103 insertions(+), 21 deletions(-)
delete mode 100644 rowers/templates/.#laboratory.html
diff --git a/rowers/alerts.py b/rowers/alerts.py
index fcd7468b..688600fc 100644
--- a/rowers/alerts.py
+++ b/rowers/alerts.py
@@ -12,7 +12,7 @@ def create_alert(manager, rower, measured,period=7, emailalert=True,
# check if manager is coach of rower. If not return 0
if manager.rower != rower:
if rower not in coach_getcoachees(manager.rower):
- return 0
+ return 0,'You are not allowed to create this alert'
m = Condition(
metric = measured['metric'],
@@ -36,7 +36,8 @@ def create_alert(manager, rower, measured,period=7, emailalert=True,
alert.save()
if 'filter' in kwargs:
- for f in filter:
+ filters = kwargs['filter']
+ for f in filters:
m = Condition(
metric = f['metric'],
value1 = f['value1'],
@@ -49,7 +50,7 @@ def create_alert(manager, rower, measured,period=7, emailalert=True,
alert.filter.add(m)
- return m.id
+ return alert.id,'Your alert was created'
@@ -71,7 +72,7 @@ def alert_add_filters(alert,filter):
alert.filter.add(m)
-
+ return 1
# get alert stats
# nperiod = 0: current period, i.e. next_run - n days to today
diff --git a/rowers/models.py b/rowers/models.py
index 7b6d824e..06bfb562 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -1030,7 +1030,20 @@ class ConditionEditForm(ModelForm):
model = Condition
fields = ['metric','condition','value1','value2']
-
+class BaseConditionFormSet(BaseFormSet):
+ def clean(self):
+ if any(self.errors):
+ return
+
+ for form in self.forms:
+ if form.cleaned_data:
+ metric = form.cleaned_data['metric']
+ condition = form.cleaned_data['condition']
+ value1 = form.cleaned_data['value1']
+ value2 = form.cleaned_data['value2']
+
+
+
rowchoices = []
for key,value in mytypes.workouttypes:
if key in mytypes.rowtypes:
diff --git a/rowers/templates/.#laboratory.html b/rowers/templates/.#laboratory.html
deleted file mode 100644
index 25889a2a..00000000
--- a/rowers/templates/.#laboratory.html
+++ /dev/null
@@ -1 +0,0 @@
-E408191@CZ27LT9RCGN72.13140:1565793987
\ No newline at end of file
diff --git a/rowers/templates/alert_create.html b/rowers/templates/alert_create.html
index 267abf40..24099c36 100644
--- a/rowers/templates/alert_create.html
+++ b/rowers/templates/alert_create.html
@@ -5,23 +5,64 @@
{% block main %}
Alert Edit
+
+
+ Alerts are useful to give you a regular update on how you are doing. For example, if you are
+ worried about rowing too short, you can set an alert on drive length, and the site will automatically
+ tell you how well you are doing.
+
+
+
+ To set an alert on a minimum drive length, you would select "Drive Length (degree)" as the metric in the
+ form below, then set the condition to ">" (greater than), and value 1 to the minimum drive length
+ that you find acceptable. The value 2 is only relevant for alerts where you want to have a metric
+ between two values. Set the workout type to "Standard Racing Shell", or whatever boat class you
+ want this metric to run for, select the period over which you want to monitor and get regular
+ reports (7 days).
+
+
+
+ Optionally, you can add filters. With filters, the alert considers only those strokes that
+ fulfill all filters. For example, you could set a filter on power between 200 and 300 Watt,
+ to only look at drive length in that power zone.
+
{% endblock %}
diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py
index 7aee9332..5d099933 100644
--- a/rowers/views/analysisviews.py
+++ b/rowers/views/analysisviews.py
@@ -4348,11 +4348,14 @@ def alerts_view(request,userid=0):
redirect_field_name=None)
def alert_create_view(request,userid=0):
r = getrequestrower(request,userid=userid)
+ FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=1)
+ filter_formset = FilterFormSet()
if request.method == 'POST':
form = AlertEditForm(request.POST)
measuredform = ConditionEditForm(request.POST)
- if form.is_valid() and measuredform.is_valid():
+ filter_formset = FilterFormSet(request.POST)
+ if form.is_valid() and measuredform.is_valid() and filter_formset.is_valid():
ad = form.cleaned_data
measured = measuredform.cleaned_data
@@ -4362,11 +4365,31 @@ def alert_create_view(request,userid=0):
workouttype = ad['workouttype']
name = ad['name']
- result = create_alert(request.user,r,measured,period=period,emailalert=emailalert,
- reststrokes=reststrokes,workouttype=workouttype,
- name=name)
+ filters = []
+
+ for filter_form in filter_formset:
+ metric = filter_form.cleaned_data.get('metric')
+ condition = filter_form.cleaned_data.get('condition')
+ value1 = filter_form.cleaned_data.get('value1')
+ value2 = filter_form.cleaned_data.get('value2')
+
+ filters.append(
+ {
+ 'metric':metric,
+ 'condition':condition,
+ 'value1':value1,
+ 'value2':value2,
+ }
+ )
+
+ result,message = create_alert(request.user,r,measured,period=period,emailalert=emailalert,
+ reststrokes=reststrokes,workouttype=workouttype,
+ filter = filters,
+ name=name)
if result:
+ messages.info(request,message)
+
url = reverse('alert_edit_view',kwargs={'id':result})
return HttpResponseRedirect(url)
else:
@@ -4391,6 +4414,7 @@ def alert_create_view(request,userid=0):
return render(request,'alert_create.html',
{
'breadcrumbs':breadcrumbs,
+ 'formset': filter_formset,
'rower':r,
'form':form,
'measuredform':measuredform,
@@ -4411,6 +4435,8 @@ def alert_edit_view(request,id=0,userid=0):
form = AlertEditForm(instance=alert)
measuredform = ConditionEditForm(instance=alert.measured)
+
+
breadcrumbs = [
{
'url':'/rowers/analysis',
diff --git a/rowers/views/statements.py b/rowers/views/statements.py
index 5392e757..06c33325 100644
--- a/rowers/views/statements.py
+++ b/rowers/views/statements.py
@@ -113,7 +113,7 @@ from rowers.models import (
)
from rowers.models import (
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet,
- get_course_timezone
+ get_course_timezone,BaseConditionFormSet,
)
from rowers.metrics import rowingmetrics,defaultfavoritecharts,nometrics
from rowers import metrics as metrics
From 729ed0d4f84d3287d16a9b3afe31f0b3d0720d43 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Sat, 17 Aug 2019 16:19:09 +0200
Subject: [PATCH 09/13] alert edit including filters
---
rowers/alerts.py | 4 +-
rowers/templates/alert_create.html | 4 +-
rowers/templates/alert_edit.html | 61 ++++++++++++++++++++++-----
rowers/views/analysisviews.py | 66 ++++++++++++++++++++++++++++--
4 files changed, 117 insertions(+), 18 deletions(-)
diff --git a/rowers/alerts.py b/rowers/alerts.py
index 688600fc..5ac22e57 100644
--- a/rowers/alerts.py
+++ b/rowers/alerts.py
@@ -55,12 +55,12 @@ def create_alert(manager, rower, measured,period=7, emailalert=True,
# update alert
-def alert_add_filters(alert,filter):
+def alert_add_filters(alert,filters):
for f in alert.filter.all():
alert.filter.remove(f)
f.delete()
- for f in filter:
+ for f in filters:
m = Condition(
metric = f['metric'],
value1 = f['value1'],
diff --git a/rowers/templates/alert_create.html b/rowers/templates/alert_create.html
index 24099c36..138e66c0 100644
--- a/rowers/templates/alert_create.html
+++ b/rowers/templates/alert_create.html
@@ -1,10 +1,10 @@
{% extends "newbase.html" %}
{% load staticfiles %}
-{% block title %}Planned Session{% endblock %}
+{% block title %}Metric Alert{% endblock %}
{% block main %}
-
Alert Edit
+
Alert Create
Alerts are useful to give you a regular update on how you are doing. For example, if you are
diff --git a/rowers/templates/alert_edit.html b/rowers/templates/alert_edit.html
index 267abf40..7c15abda 100644
--- a/rowers/templates/alert_edit.html
+++ b/rowers/templates/alert_edit.html
@@ -1,27 +1,66 @@
{% extends "newbase.html" %}
{% load staticfiles %}
-{% block title %}Planned Session{% endblock %}
+{% block title %}Metric Alert{% endblock %}
{% block main %}
-
Alert Edit
+
+ Alerts are useful to give you a regular update on how you are doing. For example, if you are
+ worried about rowing too short, you can set an alert on drive length, and the site will automatically
+ tell you how well you are doing.
+
+
+
+ To set an alert on a minimum drive length, you would select "Drive Length (degree)" as the metric in the
+ form below, then set the condition to ">" (greater than), and value 1 to the minimum drive length
+ that you find acceptable. The value 2 is only relevant for alerts where you want to have a metric
+ between two values. Set the workout type to "Standard Racing Shell", or whatever boat class you
+ want this metric to run for, select the period over which you want to monitor and get regular
+ reports (7 days).
+
+
+
+ Optionally, you can add filters. With filters, the alert considers only those strokes that
+ fulfill all filters. For example, you could set a filter on power between 200 and 300 Watt,
+ to only look at drive length in that power zone.
+
-
-
-
-
-
-
+
+
+ {% for filter_form in formset %}
+
+
+
Filter {{ forloop.counter }}
+
+ {{ filter_form.as_table }}
+
+
+
+ {% endfor %}
+
+
-
-
+
+
+
+
{% endblock %}
diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py
index 5d099933..9d744438 100644
--- a/rowers/views/analysisviews.py
+++ b/rowers/views/analysisviews.py
@@ -4430,10 +4430,69 @@ def alert_edit_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
alert = Alert.objects.get(id=id)
+
+ FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=0)
+ if len(alert.filter.all()) == 0:
+ FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet, extra=1)
+
+ filter_data = [{'metric':m.metric,
+ 'value1':m.value1,
+ 'value2':m.value2,
+ 'condition':m.condition}
+ for m in alert.filter.all()]
-
- form = AlertEditForm(instance=alert)
- measuredform = ConditionEditForm(instance=alert.measured)
+ if request.method == 'POST':
+ form = AlertEditForm(request.POST)
+ measuredform = ConditionEditForm(request.POST)
+ filter_formset = FilterFormSet(request.POST)
+ if form.is_valid() and measuredform.is_valid() and filter_formset.is_valid():
+ ad = form.cleaned_data
+ measured = measuredform.cleaned_data
+
+ period = ad['period']
+ emailalert = ad['emailalert']
+ reststrokes = ad['reststrokes']
+ workouttype = ad['workouttype']
+ name = ad['name']
+
+ m = alert.measured
+ m.metric = measured['metric']
+ m.value1 = measured['value1']
+ m.value2 = measured['value2']
+ m.condition = measured['condition']
+ m.save()
+
+ alert.period = period
+ alert.emailalert = emailalert
+ alert.reststrokes = reststrokes
+ alert.workouttype = workouttype
+ alert.name = name
+ alert.save()
+
+ filters = []
+
+ for filter_form in filter_formset:
+ metric = filter_form.cleaned_data.get('metric')
+ condition = filter_form.cleaned_data.get('condition')
+ value1 = filter_form.cleaned_data.get('value1')
+ value2 = filter_form.cleaned_data.get('value2')
+
+ filters.append(
+ {
+ 'metric':metric,
+ 'condition':condition,
+ 'value1':value1,
+ 'value2':value2,
+ }
+ )
+
+ res = alert_add_filters(alert, filters)
+ messages.info(request,'Alert was changed')
+
+ else:
+ form = AlertEditForm(instance=alert)
+ measuredform = ConditionEditForm(instance=alert.measured)
+ filter_formset = FilterFormSet(initial=filter_data)
@@ -4460,6 +4519,7 @@ def alert_edit_view(request,id=0,userid=0):
'rower':r,
'form':form,
'measuredform':measuredform,
+ 'formset':filter_formset,
})
# alert delete view
From f9231f94e02f79c7b28920c6852f2130ae22ab55 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Sat, 17 Aug 2019 17:38:39 +0200
Subject: [PATCH 10/13] further improvements to create/edit alerts
---
rowers/alerts.py | 17 +++++++++--------
rowers/models.py | 7 ++++++-
rowers/templates/alert_create.html | 6 +++---
rowers/templates/alert_edit.html | 4 ++--
4 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/rowers/alerts.py b/rowers/alerts.py
index 5ac22e57..f5bbfec4 100644
--- a/rowers/alerts.py
+++ b/rowers/alerts.py
@@ -38,16 +38,17 @@ def create_alert(manager, rower, measured,period=7, emailalert=True,
if 'filter' in kwargs:
filters = kwargs['filter']
for f in filters:
- m = Condition(
- metric = f['metric'],
- value1 = f['value1'],
- value2 = f['value2'],
- condition = f['condition']
+ if f['metric'] and f['condition']:
+ m = Condition(
+ metric = f['metric'],
+ value1 = f['value1'],
+ value2 = f['value2'],
+ condition = f['condition']
)
+
+ m.save()
- m.save()
-
- alert.filter.add(m)
+ alert.filter.add(m)
return alert.id,'Your alert was created'
diff --git a/rowers/models.py b/rowers/models.py
index 06bfb562..9caf9810 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -1022,7 +1022,7 @@ class Condition(models.Model):
)
metric = models.CharField(max_length=50,choices=parchoicesy1,verbose_name='Metric')
value1 = models.FloatField(default=0)
- value2 = models.FloatField(default=0)
+ value2 = models.FloatField(default=0,null=True,blank=True)
condition = models.CharField(max_length=20,choices=conditionchoices,null=True)
class ConditionEditForm(ModelForm):
@@ -1030,6 +1030,11 @@ class ConditionEditForm(ModelForm):
model = Condition
fields = ['metric','condition','value1','value2']
+ def clean(self):
+ cd = self.cleaned_data
+ if cd['condition'] == 'between' and cd['value2'] is None:
+ raise forms.ValidationError('When using between, you must fill value 1 and value 2')
+
class BaseConditionFormSet(BaseFormSet):
def clean(self):
if any(self.errors):
diff --git a/rowers/templates/alert_create.html b/rowers/templates/alert_create.html
index 138e66c0..d7b1c57a 100644
--- a/rowers/templates/alert_create.html
+++ b/rowers/templates/alert_create.html
@@ -41,16 +41,16 @@
- {% for filter_form in formset %}