diff --git a/rowers/forms.py b/rowers/forms.py
index 8610dab5..bad4b1b6 100644
--- a/rowers/forms.py
+++ b/rowers/forms.py
@@ -150,6 +150,7 @@ class InstantPlanSelectForm(forms.Form):
# Instroke Metrics interactive chart form
class InstrokeForm(forms.Form):
+ name = forms.CharField(initial="", max_length=200,required=False)
metric = forms.ChoiceField(label='metric',choices=(('a','a'),('b','b')))
individual_curves = forms.BooleanField(label='individual curves',initial=False,
required=False)
@@ -159,6 +160,9 @@ class InstrokeForm(forms.Form):
required=False, initial=0, widget=forms.HiddenInput())
activeminutesmax = forms.IntegerField(
required=False, initial=0, widget=forms.HiddenInput())
+ notes = forms.CharField(required=False,
+ max_length=200, label='Notes',
+ widget=forms.Textarea)
def __init__(self, *args, **kwargs): # pragma: no cover
choices = kwargs.pop('choices', [])
diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py
index 063ff6f0..93366b79 100644
--- a/rowers/interactiveplots.py
+++ b/rowers/interactiveplots.py
@@ -4080,7 +4080,8 @@ def interactive_streamchart(id=0, promember=0):
def instroke_interactive_chart(df,metric, workout, spm_min, spm_max,
activeminutesmin, activeminutesmax,
- individual_curves):
+ individual_curves,
+ name='',notes=''):
df_pos = (df+abs(df))/2.
@@ -4184,6 +4185,24 @@ def instroke_interactive_chart(df,metric, workout, spm_min, spm_max,
plot.add_layout(label)
plot.add_layout(label2)
+ if name:
+ namelabel = Label(x=50, y=480, x_units='screen', y_units='screen',
+ text=name,
+ background_fill_alpha=0.7,
+ background_fill_color='white',
+ text_color='black',
+ )
+ plot.add_layout(namelabel)
+
+ if notes:
+ noteslabel = Label(x=50, y=50, x_units='screen', y_units='screen',
+ text=notes,
+ background_fill_alpha=0.7,
+ background_fill_color='white',
+ text_color='black',
+ )
+ plot.add_layout(noteslabel)
+
if individual_curves:
for index,row in df.iterrows():
plot.line(xvals,row,color='lightgray',line_width=1)
diff --git a/rowers/models.py b/rowers/models.py
index 8e6cfba1..3881e269 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -4969,10 +4969,12 @@ class ShareKey(models.Model):
class InStrokeAnalysis(models.Model):
workout = models.ForeignKey(Workout, on_delete=models.CASCADE)
+ rower = models.ForeignKey(Rower, on_delete=models.CASCADE)
+ metric = models.CharField(max_length=140, blank=True, null=True)
name = models.CharField(max_length=150, blank=True, null=True)
date = models.DateField(blank=True, null=True)
notes = models.TextField(blank=True)
start_second = models.IntegerField(default=0)
end_second = models.IntegerField(default=3600)
- min_spm = models.IntegerField(default=10)
- max_spm = models.IntegerField(default=45)
+ spm_min = models.IntegerField(default=10)
+ spm_max = models.IntegerField(default=45)
diff --git a/rowers/templates/instroke_analysis.html b/rowers/templates/instroke_analysis.html
new file mode 100644
index 00000000..5e98c069
--- /dev/null
+++ b/rowers/templates/instroke_analysis.html
@@ -0,0 +1,55 @@
+{% extends "newbase.html" %}
+{% load static %}
+{% load rowerfilters %}
+
+{% block title %}Rowsandall - Analysis {% endblock %}
+
+{% block main %}
+
+
In-Stroke Analysis for {{ rower.user.first_name }} {{ rower.user.last_name }}
+
+
+
+ {% if analyses %}
+ {% for analysis in analyses %}
+ -
+
{{ analysis.name }}
+
+
+
+
+
+ {{ analysis.notes }}
+
+
+ Workout: {{ analysis.workout }}
+
+
+ {{ analysis.spm_min }} - {{ analysis.spm_max }} SPM,
+ {{ analysis.start_second|secondstotimestring }} - {{ analysis.end_second|secondstotimestring }}
+
+
+
+ {{ analysis.date }}
+
+
+
+ {% endfor %}
+ {% else %}
+ -
+
You have not saved any analyses for {{ rower.user.first_name }}
+
+ {% endif %}
+
+
+
+
+{% endblock %}
+
+{% block scripts %}
+{% endblock %}
+
+{% block sidebar %}
+{% include 'menu_analytics.html' %}
+{% endblock %}
diff --git a/rowers/templates/instroke_interactive.html b/rowers/templates/instroke_interactive.html
index db6aaa5d..7eec7665 100644
--- a/rowers/templates/instroke_interactive.html
+++ b/rowers/templates/instroke_interactive.html
@@ -143,7 +143,8 @@ $( function() {
-
+
+
diff --git a/rowers/templates/laboratory.html b/rowers/templates/laboratory.html
index 15b27e02..8eeab9c3 100644
--- a/rowers/templates/laboratory.html
+++ b/rowers/templates/laboratory.html
@@ -12,7 +12,7 @@
Rower: {{ rower.user.first_name }}
- Try out Alerts
+ Try out In-Stroke Analysis
{% endblock %}
diff --git a/rowers/urls.py b/rowers/urls.py
index 51364854..a43558f1 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -828,6 +828,8 @@ urlpatterns = [
re_path(r'^errormessage/(?P[\w\ ]+.*)/$',
views.errormessage_view, name='errormessage_view'),
re_path(r'^analysis/$', views.analysis_view, name='analysis'),
+ re_path(r'^analysis/instrokeanalysis/$', views.instrokeanalysis_view,
+ name='instrokeanalysis_view'),
re_path(r'^promembership', TemplateView.as_view(
template_name='promembership.html'), name='promembership'),
re_path(r'^checkout/(?P\d+)/$',
diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py
index 66f96a62..6251ead6 100644
--- a/rowers/views/analysisviews.py
+++ b/rowers/views/analysisviews.py
@@ -1845,6 +1845,32 @@ def agegrouprecordview(request, sex='male', weightcategory='hwt',
})
+@login_required
+@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True)
+def instrokeanalysis_view(request, userid=0):
+ r = getrequestrower(request, userid=userid)
+
+ analyses = InStrokeAnalysis.objects.filter(rower=r).order_by("-date")
+
+ breadcrumbs = [
+ {
+ 'url': '/rowers/analysis',
+ 'name': 'Analysis'
+ },
+ {
+ 'url': reverse('instrokeanalysis_view'),
+ 'name': 'In-Stroke Analysis',
+ },
+ ]
+
+ return render(request, 'instroke_analysis.html',
+ {
+ 'breadcrumbs': breadcrumbs,
+ 'analyses': analyses,
+ 'rower': r,
+ })
+
+
@login_required
@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True)
def alerts_view(request, userid=0):
diff --git a/rowers/views/statements.py b/rowers/views/statements.py
index 01709aec..628087b1 100644
--- a/rowers/views/statements.py
+++ b/rowers/views/statements.py
@@ -153,7 +153,7 @@ from rowers.models import (
VideoAnalysis, ShareKey,
StandardCollection, CourseStandard,
VirtualRaceFollower, TombStone, InstantPlan,
- PlannedSessionStep,
+ PlannedSessionStep,InStrokeAnalysis,
)
from rowers.models import (
RowerPowerForm, RowerHRZonesForm, RowerForm, RowerCPForm, GraphImage, AdvancedWorkoutForm,
diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py
index a3155e52..1e9f7cda 100644
--- a/rowers/views/workoutviews.py
+++ b/rowers/views/workoutviews.py
@@ -2981,6 +2981,8 @@ def instroke_chart_interactive(request, id=0):
metric = instrokemetrics[0]
spm_min = 15
spm_max = 45
+ name = ''
+ notes = ''
activeminutesmax = int(rowdata.duration/60.)
activeminutesmin = 0
@@ -2996,6 +2998,24 @@ def instroke_chart_interactive(request, id=0):
activeminutesmin = form.cleaned_data['activeminutesmin']
activeminutesmax = form.cleaned_data['activeminutesmax']
individual_curves = form.cleaned_data['individual_curves']
+ notes = form.cleaned_data['notes']
+ name = form.cleaned_data['name']
+
+ if "_save" in request.POST:
+ instroke_analysis = InStrokeAnalysis(
+ workout = w,
+ metric = metric,
+ name = name,
+ date = timezone.now().date(),
+ notes = notes,
+ start_second = 60*activeminutesmin,
+ end_second = 60*activeminutesmax,
+ spm_min = spm_min,
+ spm_max = spm_max,
+ rower=w.user,
+ )
+ instroke_analysis.save()
+ messages.info(request,'In-Stroke Analysis saved')
activesecondsmin = 60.*activeminutesmin
@@ -3016,6 +3036,7 @@ def instroke_chart_interactive(request, id=0):
activeminutesmin,
activeminutesmax,
individual_curves,
+ name=name,notes=notes,
)
# change to range spm_min to spm_max