diff --git a/rowers/forms.py b/rowers/forms.py
index 30b5e623..542f7cba 100644
--- a/rowers/forms.py
+++ b/rowers/forms.py
@@ -28,6 +28,7 @@ from rowers.utils import landingpages
from rowers.metrics import axes, metricsgroups, rowingmetrics
from rowers.metrics import axlabels
from rowers.rower_rules import user_is_not_basic
+from rowers import models
formaxlabels = axlabels.copy()
formaxlabels.pop('None')
@@ -401,7 +402,7 @@ class StandardsForm(forms.Form):
class DocumentsForm(forms.Form):
- rpechoices = Workout.rpechoices
+ rpechoices = models.rpechoices
rpechoices = tuple([(-1, '---')]+list(rpechoices))
title = forms.CharField(required=False)
file = forms.FileField(required=False,
diff --git a/rowers/models.py b/rowers/models.py
index 53f5034d..8de5fe33 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -3421,7 +3421,22 @@ class PlannedSessionFormSmall(ModelForm):
boattypes = mytypes.boattypes
# Workout
-
+rpechoices = (
+ (0, 'Not Specified'),
+ (1, '1 Very Easy (a walk in the park)'), # 20 TSS / hour
+ (2, '2 Easy (You breathe normally, it feels comfortable)'), # 30 TSS / hour
+ (3, '3 Somewhat easy (You can talk easily but did you notice the beautiful clouds?)'),
+ # 50 TSS/hour
+ (4, '4 Moderate (You can talk in short spurts, breathing more labored, this feels just right)'),
+ (5, "5 (It's not that painful, you just don't want to be here all day.)"),
+ (6, '6 Somewhat Hard (You can say a few words if you need to)'), # 70 TSS / hour
+ (7, '7 Vigorous (This is starting to get painful)'),
+ # 100 TSS / hour
+ (8, "8 Hard (You can barely talk, breathing heavily, hoping you won't have to this that long)"),
+ (9, '9 Very Hard (My goodness, please make it stop)'), # 120 TSS / hour
+ # 140 TSS / hour
+ (10, '10 Max Effort (You can barely remember your name, you would rather rip out your toenails than go through this)')
+)
class Workout(models.Model):
workouttypes = mytypes.workouttypes
@@ -3429,22 +3444,7 @@ class Workout(models.Model):
privacychoices = mytypes.privacychoices
adaptivetypes = mytypes.adaptivetypes
boatbrands = mytypes.boatbrands
- rpechoices = (
- (0, 'Not Specified'),
- (1, '1 Very Easy (a walk in the park)'), # 20 TSS / hour
- (2, '2 Easy (You breathe normally, it feels comfortable)'), # 30 TSS / hour
- (3, '3 Somewhat easy (You can talk easily but did you notice the beautiful clouds?)'),
- # 50 TSS/hour
- (4, '4 Moderate (You can talk in short spurts, breathing more labored, this feels just right)'),
- (5, "5 (It's not that painful, you just don't want to be here all day.)"),
- (6, '6 Somewhat Hard (You can say a few words if you need to)'), # 70 TSS / hour
- (7, '7 Vigorous (This is starting to get painful)'),
- # 100 TSS / hour
- (8, "8 Hard (You can barely talk, breathing heavily, hoping you won't have to this that long)"),
- (9, '9 Very Hard (My goodness, please make it stop)'), # 120 TSS / hour
- # 140 TSS / hour
- (10, '10 Max Effort (You can barely remember your name, you would rather rip out your toenails than go through this)')
- )
+
user = models.ForeignKey(Rower, on_delete=models.CASCADE)
team = models.ManyToManyField(Team, blank=True)
@@ -3586,7 +3586,11 @@ class Workout(models.Model):
return stri
-
+class WorkoutRPEForm(ModelForm):
+ class Meta:
+ model = Workout
+ fields = ['rpe']
+
class TombStone(models.Model):
user = models.ForeignKey(Rower, on_delete=models.CASCADE)
uploadedtoc2 = models.IntegerField(default=0)
diff --git a/rowers/tasks.py b/rowers/tasks.py
index 19afa798..cf6c0c23 100644
--- a/rowers/tasks.py
+++ b/rowers/tasks.py
@@ -3187,7 +3187,8 @@ def df_from_summary(data):
@app.task
-def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec, defaulttimezone, debug=False, **kwargs):
+def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec,
+ defaulttimezone, debug=False, **kwargs):
time.sleep(delaysec)
dologging('c2_import.log',str(c2id)+' for userid '+str(userid))
data = alldata[c2id]
diff --git a/rowers/templates/list_workouts.html b/rowers/templates/list_workouts.html
index 02faeb3a..ec2f3f32 100644
--- a/rowers/templates/list_workouts.html
+++ b/rowers/templates/list_workouts.html
@@ -92,6 +92,11 @@
{% if workout.rankingpiece %}
{% endif %}
+ {% if workout.rpe == 0 %}
+
+ No RPE
+
+ {% endif %}
{% if workout.name != '' %}
diff --git a/rowers/templates/workouts_rpe.html b/rowers/templates/workouts_rpe.html
new file mode 100644
index 00000000..006d89d9
--- /dev/null
+++ b/rowers/templates/workouts_rpe.html
@@ -0,0 +1,209 @@
+{% extends "newbase.html" %}
+{% load static %}
+{% load rowerfilters %}
+
+{% block title %}Rowsandall Workouts List{% endblock %}
+
+{% block scripts %}
+
+
+
+{% endblock %}
+
+{% block main %}
+
+
+
+
+ Workouts of {{ rower.user.first_name }} {{ rower.user.last_name }}
+
+
+
+
+ -
+
+ {% if workouts.has_previous %}
+ {% if request.GET.q %}
+
+
+
+
+
+
+ {% else %}
+
+
+
+
+
+
+ {% endif %}
+ {% endif %}
+
+
+
+ -
+
+
+
+
+ {% if workouts %}
+ {% for workout in workouts %}
+ -
+ {{ workout.date|date:"Y-m-d" }} {{ workout.starttime|date:"H:i" }}
+ {% if workout.duplicate %}
+
+ {% endif %}
+ {% if workout.rankingpiece %}
+
+ {% endif %}
+ {% if workout.rpe == 0 %}
+
+ No RPE
+
+ {% endif %}
+
+ {% if workout.name != '' %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+ {% with workout.workouttype|icon|safe as templateName %}
+ {% include templateName %}
+ {% endwith %}
+
+
+ Distance
+ {{ workout.distance|distanceprint }}
+
+
+ Time
+ {{ workout.duration |durationprint:"%H:%M:%S.%f" }}
+
+
+ {% if workout|may_edit:request %}
+
+
+ {% else %}
+
+ {% endif %}
+
+
+ {% if workout|may_edit:request %}
+ {% if rower.defaultlandingpage2 != 'workout_delete' %}
+
+
+ {% else %}
+
+
+ {% endif %}
+ {% else %}
+
+ {% endif %}
+
+
+
+
+ {% endfor %}
+
+ {% else %}
+ - No workouts found
+ {% endif %}
+
+
+{% endblock %}
+
+{% block sidebar %}
+{% include 'menu_workouts.html' %}
+{% endblock %}
diff --git a/rowers/urls.py b/rowers/urls.py
index 5796a45c..fe555ae3 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -286,6 +286,8 @@ urlpatterns = [
views.agegrouprecordview, name='agegrouprecordview'),
re_path(r'^agegrouprecords/$',
views.agegrouprecordview, name='agegrouprecordview'),
+ re_path(r'^workouts/setrpe/$', views.workouts_setrpe_view,
+ name='workouts_setrpe_view'),
re_path(r'^list-workouts/team/(?P\d+)/$', views.workouts_view,
name='workouts_view'),
re_path(r'^(?P\d+)/list-workouts/$', views.workouts_view,
diff --git a/rowers/views/statements.py b/rowers/views/statements.py
index 036aff86..9aa224db 100644
--- a/rowers/views/statements.py
+++ b/rowers/views/statements.py
@@ -159,22 +159,20 @@ from rowers.models import (
VirtualRaceFollower, TombStone, InstantPlan,
PlannedSessionStep,InStrokeAnalysis, ForceCurveAnalysis
)
-from rowers.models import (
- RowerPowerForm, RowerHRZonesForm, RowerForm, RowerCPForm, GraphImage, AdvancedWorkoutForm,
- RowerPowerZonesForm, AccountRowerForm, UserForm,
- Team, TeamForm, TeamInviteForm, TeamInvite, TeamRequest,
- WorkoutComment, WorkoutCommentForm, RowerExportForm,
- CalcAgePerformance,
- PowerTimeFitnessMetric, BlogPost,
- PlannedSessionForm, PlannedSessionTemplateForm,
- PlannedSessionFormSmall, GeoCourseEditForm, VirtualRace,
- VirtualRaceForm, VirtualRaceResultForm, RowerImportExportForm,
- IndoorVirtualRaceResultForm, IndoorVirtualRaceResult,
- IndoorVirtualRaceForm, PlannedSessionCommentForm,
- Alert, Condition, StaticChartRowerForm,
- FollowerForm, VirtualRaceAthleteForm, InstantPlanForm, DataRowerForm,
- StepEditorForm,
-)
+from rowers.models import ( RowerPowerForm, RowerHRZonesForm,
+ RowerForm, RowerCPForm, GraphImage, AdvancedWorkoutForm,
+ RowerPowerZonesForm, AccountRowerForm, UserForm, Team, TeamForm,
+ TeamInviteForm, TeamInvite, TeamRequest, WorkoutComment,
+ WorkoutCommentForm, RowerExportForm, CalcAgePerformance,
+ PowerTimeFitnessMetric, BlogPost, PlannedSessionForm,
+ PlannedSessionTemplateForm, PlannedSessionFormSmall,
+ GeoCourseEditForm, VirtualRace, VirtualRaceForm,
+ VirtualRaceResultForm, RowerImportExportForm, WorkoutRPEForm,
+ IndoorVirtualRaceResultForm, IndoorVirtualRaceResult,
+ IndoorVirtualRaceForm, PlannedSessionCommentForm, Alert,
+ Condition, StaticChartRowerForm, FollowerForm,
+ VirtualRaceAthleteForm, InstantPlanForm, DataRowerForm,
+ StepEditorForm, )
from rowers.models import (
FavoriteForm, BaseFavoriteFormSet, SiteAnnouncement, BasePlannedSessionFormSet,
get_course_timezone, BaseConditionFormSet,
diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py
index dbc5e220..d60fbffd 100644
--- a/rowers/views/workoutviews.py
+++ b/rowers/views/workoutviews.py
@@ -1921,6 +1921,99 @@ def plannedsession_compare_view(request, id=0, userid=0):
return HttpResponseRedirect(url)
+# set RPE for list of workouts
+@login_required()
+def workouts_setrpe_view(request):
+ startdate, enddate = get_dates_timeperiod(
+ request, defaulttimeperiod='lastyear')
+ r = getrequestrower(request)
+
+ startdate = datetime.datetime.combine(startdate, datetime.time())
+ enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59))
+
+
+ workouts = Workout.objects.filter(
+ user=r,rpe=0,
+ startdatetime__gte=startdate,
+ startdatetime__lte=enddate
+ ).order_by("-date", "-starttime")
+
+ dateform = DateRangeForm(initial={
+ 'startdate': startdate,
+ 'enddate': enddate,
+ })
+ WorkoutsRPEFormSet = modelformset_factory(
+ Workout,
+ form=WorkoutRPEForm,
+ )
+
+
+ if request.method == 'POST':
+ dateform = DateRangeForm(request.POST)
+ rpe_formset = WorkoutsRPEFormSet(request.POST)
+ if dateform.is_valid(): # pragma: no cover
+ startdate = dateform.cleaned_data['startdate']
+ enddate = dateform.cleaned_data['enddate']
+ if rpe_formset.is_valid():
+ for item in rpe_formset.cleaned_data:
+ rpe = item['rpe']
+ workout = item['id']
+ if workout:
+ workout.rpe = rpe
+ workout.save()
+ workouts = Workout.objects.filter(
+ user=r,rpe=0,
+ startdatetime__gte=startdate,
+ startdatetime__lte=enddate
+ ).order_by("-date", "-starttime")
+
+ rpe_formset = WorkoutsRPEFormSet(queryset=workouts)
+
+ usertimezone = pytz.timezone(r.defaulttimezone)
+ startdate = datetime.datetime.combine(
+ startdate, datetime.time()).astimezone(usertimezone)
+ enddate = datetime.datetime.combine(
+ enddate, datetime.time(23, 59, 59)).astimezone(usertimezone)
+
+ if enddate < startdate: # pragma: no cover
+ s = enddate
+ enddate = startdate
+ startdate = s
+
+
+
+ today = timezone.now()
+ announcements = SiteAnnouncement.objects.filter(
+ expires__gte=today
+ ).order_by(
+ "-created",
+ "-id"
+ )
+
+
+ breadcrumbs = [
+ {
+ 'url': '/rowers/list-workouts/',
+ 'name': 'Workouts'
+ },
+ {
+ 'url': '/rowers/workouts/setrpe/',
+ 'name': 'Set RPE'
+ }
+ ]
+
+ return render(request,'workouts_rpe.html',
+ {
+ 'workouts': workouts,
+ 'active': 'nav-workouts',
+ 'rower': r,
+ 'breadcrumbs': breadcrumbs,
+ 'dateform': dateform,
+ 'startdate': startdate,
+ 'enddate': enddate,
+ 'rpe_formset': rpe_formset,
+ })
+
# List Workouts
@login_required()
@@ -2111,6 +2204,12 @@ def workouts_view(request, message='', successmessage='',
]
timeperiod = startdate.strftime(
'%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
+
+ norpecount = len([w for w in workouts if w.rpe==0])
+ if norpecount:
+ messages.info(request,'You have workouts with no RPE value set. \
+ Click here to update them.')
+
return render(request, 'list_workouts.html',
{'workouts': workouts,
'active': 'nav-workouts',