From b4973d8f521b8ca47a012c9d5c9ffb4815255389 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 25 Nov 2022 17:14:19 +0100 Subject: [PATCH] adding rpe editing --- rowers/forms.py | 3 +- rowers/models.py | 40 +++--- rowers/tasks.py | 3 +- rowers/templates/list_workouts.html | 5 + rowers/templates/workouts_rpe.html | 209 ++++++++++++++++++++++++++++ rowers/urls.py | 2 + rowers/views/statements.py | 30 ++-- rowers/views/workoutviews.py | 99 +++++++++++++ 8 files changed, 355 insertions(+), 36 deletions(-) create mode 100644 rowers/templates/workouts_rpe.html 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 %} + + +
  • +
  • +
    + {% csrf_token %} + {{ rpe_formset.management_form }} + + + + + + + {% for field in rpe_formset.0.visible_fields %} + + {% endfor %} + + + + {% for form in rpe_formset %} + + + + + {% if form.instance.pk %} + {% for field in form.visible_fields %} + + {% endfor %} + {% endif %} + + {% endfor %} + +
       {{ field.label_tag }}
    + {{ form.id }} + {% for field in form.hidden_fields %} + {{ field }} + {% endfor %} + + {% if form.instance.pk %}{{ form.instance.startdatetime }}{% endif %} + + {% if form.instance.pk %}{{ form.instance.name }}{% endif %} + + {{ field }} +
    + +
    +
  • + + + {% 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 != '' %} +

    + {% if team %} + + {{ workout.name }} ({{ workout.user.user.first_name }} {{ workout.user.user.last_name}}) + + {% else %} + + {{ workout.name }} + + {% endif %} +

    + {% else %} +

    + {% if team %} + + No Name ({{ workout.user.user.first_name }} {{ workout.user.user.last_name}}) + + {% else %} + + No Name + + {% endif %} +

    + {% 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',