From 63285a686da66a186fc5c9f026c1522e5b5adf2a Mon Sep 17 00:00:00 2001 From: bencyn Date: Thu, 13 Jun 2019 19:24:19 +0300 Subject: [PATCH] 166152333-story(manager-workouts): add support for macro, meso and macro cycles - Add workout cycle fixtures in weeks and month as well days - Add workout_plan field in core_days_of_week model - Add cycle options field in manager_workout model - Add set cycle type modal link in single workout view [Delivers #166152333] --- wger/core/fixtures/days_of_week.json | 205 +++++++++++++++--- .../0004_daysofweek_workout_plan.py | 18 ++ wger/core/models.py | 2 + wger/core/templates/tags/render_day.html | 188 ++++++++-------- .../migrations/0002_workout_cycle_type.py | 18 ++ wger/manager/models.py | 8 + wger/manager/templates/workout/view.html | 146 ++++++++----- wger/manager/tests/test_workout.py | 2 +- wger/manager/views/day.py | 50 ++++- 9 files changed, 459 insertions(+), 178 deletions(-) create mode 100644 wger/core/migrations/0004_daysofweek_workout_plan.py create mode 100644 wger/manager/migrations/0002_workout_cycle_type.py diff --git a/wger/core/fixtures/days_of_week.json b/wger/core/fixtures/days_of_week.json index 2b48aadc..27e35039 100644 --- a/wger/core/fixtures/days_of_week.json +++ b/wger/core/fixtures/days_of_week.json @@ -1,51 +1,202 @@ [ { - "pk": 1, - "model": "core.daysofweek", + "pk": 1, + "model": "core.daysofweek", "fields": { - "day_of_week": "Monday" + "day_of_week": "Monday", + "workout_plan": "day" } - }, + }, { - "pk": 2, - "model": "core.daysofweek", + "pk": 2, + "model": "core.daysofweek", "fields": { - "day_of_week": "Tuesday" + "day_of_week": "Tuesday", + "workout_plan": "day" } - }, + }, { - "pk": 3, - "model": "core.daysofweek", + "pk": 3, + "model": "core.daysofweek", "fields": { - "day_of_week": "Wednesday" + "day_of_week": "Wednesday", + "workout_plan": "day" } - }, + }, { - "pk": 4, - "model": "core.daysofweek", + "pk": 4, + "model": "core.daysofweek", "fields": { - "day_of_week": "Thursday" + "day_of_week": "Thursday", + "workout_plan": "day" } - }, + }, { - "pk": 5, - "model": "core.daysofweek", + "pk": 5, + "model": "core.daysofweek", "fields": { - "day_of_week": "Friday" + "day_of_week": "Friday", + "workout_plan": "day" } - }, + }, { - "pk": 6, - "model": "core.daysofweek", + "pk": 6, + "model": "core.daysofweek", "fields": { - "day_of_week": "Saturday" + "day_of_week": "Saturday", + "workout_plan": "day" } - }, + }, { - "pk": 7, - "model": "core.daysofweek", + "pk": 7, + "model": "core.daysofweek", "fields": { - "day_of_week": "Sunday" + "day_of_week": "Sunday", + "workout_plan": "day" + } + }, + { + "pk": 8, + "model": "core.daysofweek", + "fields": { + "day_of_week": "Week 1", + "workout_plan": "week" + } + }, + { + "pk": 9, + "model": "core.daysofweek", + "fields": { + "day_of_week": "Week 2", + "workout_plan": "week" + } + }, + { + "pk": 10, + "model": "core.daysofweek", + "fields": { + "day_of_week": "Week 3", + "workout_plan": "week" + } + }, + { + "pk": 11, + "model": "core.daysofweek", + "fields": { + "day_of_week": "Week 4", + "workout_plan": "week" + } + }, + { + "pk": 12, + "model": "core.daysofweek", + "fields": { + "day_of_week": "Week 5", + "workout_plan": "week" + } + }, + { + "pk": 13, + "model": "core.daysofweek", + "fields": { + "day_of_week": "Week 6", + "workout_plan": "week" + } + }, + { + "pk": 14, + "model": "core.daysofweek", + "fields": { + "day_of_week": "January", + "workout_plan": "Month" + } + }, + { + "pk": 15, + "model": "core.daysofweek", + "fields": { + "day_of_week": "February", + "workout_plan": "Month" + } + }, + { + "pk": 16, + "model": "core.daysofweek", + "fields": { + "day_of_week": "March", + "workout_plan": "Month" + } + }, + { + "pk": 17, + "model": "core.daysofweek", + "fields": { + "day_of_week": "April", + "workout_plan": "Month" + } + }, + { + "pk": 18, + "model": "core.daysofweek", + "fields": { + "day_of_week": "May", + "workout_plan": "Month" + } + }, + { + "pk": 17, + "model": "core.daysofweek", + "fields": { + "day_of_week": "June", + "workout_plan": "Month" + } + }, + { + "pk": 18, + "model": "core.daysofweek", + "fields": { + "day_of_week": "July", + "workout_plan": "Month" + } + }, + { + "pk": 19, + "model": "core.daysofweek", + "fields": { + "day_of_week": "August", + "workout_plan": "Month" + } + }, + { + "pk": 20, + "model": "core.daysofweek", + "fields": { + "day_of_week": "September", + "workout_plan": "Month" + } + }, + { + "pk": 21, + "model": "core.daysofweek", + "fields": { + "day_of_week": "October", + "workout_plan": "Month" + } + }, + { + "pk": 22, + "model": "core.daysofweek", + "fields": { + "day_of_week": "November", + "workout_plan": "Month" + } + }, + { + "pk": 23, + "model": "core.daysofweek", + "fields": { + "day_of_week": "December", + "workout_plan": "Month" } } ] \ No newline at end of file diff --git a/wger/core/migrations/0004_daysofweek_workout_plan.py b/wger/core/migrations/0004_daysofweek_workout_plan.py new file mode 100644 index 00000000..0966a309 --- /dev/null +++ b/wger/core/migrations/0004_daysofweek_workout_plan.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.1 on 2019-06-13 13:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_userprofile_user_can_create_users'), + ] + + operations = [ + migrations.AddField( + model_name='daysofweek', + name='workout_plan', + field=models.CharField(max_length=5, null=True), + ), + ] diff --git a/wger/core/models.py b/wger/core/models.py index e844611b..284bab8c 100644 --- a/wger/core/models.py +++ b/wger/core/models.py @@ -512,6 +512,8 @@ class DaysOfWeek(models.Model): day_of_week = models.CharField(max_length=9, verbose_name=_('Day of the week')) + workout_plan = models.CharField(max_length=5, null=True) + class Meta: ''' Order by day-ID, this is needed for some DBs diff --git a/wger/core/templates/tags/render_day.html b/wger/core/templates/tags/render_day.html index 68eaf496..dc4d69ae 100644 --- a/wger/core/templates/tags/render_day.html +++ b/wger/core/templates/tags/render_day.html @@ -1,40 +1,37 @@ {% load i18n staticfiles wger_extras thumbnail %} - - - + + - + {{ day.days_of_week.text }} – {{ day.obj.description }} + {% if editable %} + + + + + + + + + {% endif %} + + {% for set in day.set_list %} @@ -43,77 +40,74 @@
{{ forloop.counter }}
{% if editable %} - + {% if day.set_list|length > 1 %} - -
+ +
{% endif %} - - -
- - -
+ + +
+ + +
{% endif %} @@ -121,22 +115,36 @@

{% if editable %}

{% endif %} - {% endfor %} + {% endfor %} + {% if editable %} {% endif %} -
+
- {{ day.days_of_week.text }} – {{ day.obj.description }} - {% if editable %} - - - - - - - - - {% endif %} -
- {% for exercise in set.exercise_list %} -
-
+ {% for exercise in set.exercise_list %} +
+
-
- - -
-

- {{ exercise.obj.name }} -

-

{{exercise.setting_text}}

+
+

+ {{ exercise.obj.name }} +

+

{{exercise.setting_text}}

- {% if editable %} - {% if not exercise.setting_list %} -

- {% trans "This exercise has no repetitions." %}
- - {% trans "Edit them now."%} - -

- {% endif %} + {% if editable %} + {% if not exercise.setting_list %} +

+ {% trans "This exercise has no repetitions." %}
+ + {% trans "Edit them now."%} + +

+ {% endif %} - {% if exercise.comment_list %} -

- {% for comment in exercise.comment_list %} + {% if exercise.comment_list %} +

+ {% for comment in exercise.comment_list %} {{comment}}
- {% endfor %} -

- {% endif %} - {% endif %} + {% endfor %} +

+ {% endif %} + {% endif %} +
-
- {% endfor %} + {% endfor %} +
- - {% trans "No exercises selected for this day." %} - {% trans "Add one now." %} - + + {% if "Microcycle" in day.obj.training.cycle_type %} + {% trans "No exercises selected for this day." %} + {% elif "Mesocycle" in day.obj.training.cycle_type %} + {% trans "No exercises selected for this week." %} + {% elif "Macrocycle" in day.obj.training.cycle_type %} + {% trans "No exercises selected for this month." %} + {% endif %} + {% trans "Add one now." %} +
- {% trans "Add exercises to this workout day" %} + + {% if 'Microcycle' in day.obj.training.cycle_type %} + {% trans "Add exercises to this workout day" %} + {% elif 'Mesocycle' in day.obj.training.cycle_type %} + {% trans "Add exercises to this workout week" %} + {% elif 'Macrocycle' in day.obj.training.cycle_type %} + {% trans "Add exercises to this workout month" %} + {% endif %} +
+ \ No newline at end of file diff --git a/wger/manager/migrations/0002_workout_cycle_type.py b/wger/manager/migrations/0002_workout_cycle_type.py new file mode 100644 index 00000000..7cf352a4 --- /dev/null +++ b/wger/manager/migrations/0002_workout_cycle_type.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.1 on 2019-06-13 14:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('manager', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='workout', + name='cycle_type', + field=models.CharField(choices=[('Microcycle', 'Microcycle - one week plan'), ('Mesocycle', 'Mesocycle - Two to six weeks plan'), ('Macrocycle', 'Macrocycle - one year plan')], default='', max_length=10), + ), + ] diff --git a/wger/manager/models.py b/wger/manager/models.py index fbd81e55..2da6bcb4 100644 --- a/wger/manager/models.py +++ b/wger/manager/models.py @@ -53,6 +53,12 @@ class Workout(models.Model): Model for a training schedule ''' + CYCLE_OPTIONS = [ + ('Microcycle', 'Microcycle - one week plan'), + ('Mesocycle', 'Mesocycle - Two to six weeks plan'), + ('Macrocycle', 'Macrocycle - one year plan') + ] + class Meta: ''' Meta class to set some other properties @@ -67,6 +73,8 @@ class Meta: "example 'Focus on back' or 'Week 1 of program xy'.")) user = models.ForeignKey(User, verbose_name=_('User'), on_delete=models.CASCADE) + cycle_type = models.CharField(max_length=10, choices=CYCLE_OPTIONS, default='') + def get_absolute_url(self): ''' Returns the canonical URL to view a workout diff --git a/wger/manager/templates/workout/view.html b/wger/manager/templates/workout/view.html index 376dca4e..84174c16 100644 --- a/wger/manager/templates/workout/view.html +++ b/wger/manager/templates/workout/view.html @@ -7,9 +7,9 @@ {# Opengraph #} {# #} {% block opengraph %} - {{ block.super }} - - +{{ block.super }} + + {% endblock %} {# #} @@ -23,24 +23,23 @@ {# #} {% block header %} {% endblock %} @@ -58,26 +57,57 @@

{% endif %} +{% if workout.cycle_type %} +

+ {% trans "Plan" %}: {{workout.cycle_type}} +

+{% endif %} + {% for day in workout.canonical_representation.day_list %} -
- {% render_day day.obj is_owner %} -
+
+ {% render_day day.obj is_owner %} +
{% empty %} - {% if is_owner %} -

- - {% trans "No days for this workout." %} - {% trans "Add training day" %} - -

- {% endif %} +{% if is_owner %} +{% if not workout.cycle_type %} +

+ + {% trans "Set a cycle type and optionally a goal for this workout" %} + +

+{% else %} +

+ + {% if "Microcycle" in workout.cycle_type %} + {% trans "No days for this workout." %} + {% trans "Add training day" %} + {% elif "Mesocycle" in workout.cycle_type %} + {% trans "No weeks for this workout." %} + {% trans "Add training week" %} + {% elif "Macrocycle" in workout.cycle_type %} + {% trans "No months for this workout." %} + {% trans "Add training month" %} + {% endif %} + +

+{% endif %} +{% endif %} {% endfor %} {% if is_owner %}

- {% trans "Add training day" %} - | - {% trans "Show/Hide exercise comments" %} + + + {% if "Microcycle" in workout.cycle_type %} + {% trans "Add training day" %} + {% elif "Mesocycle" in workout.cycle_type %} + {% trans "Add training week" %} + {% elif "Macrocycle" in workout.cycle_type %} + {% trans "Add training month" %} + {% endif %} + + {% trans "Show/Hide exercise comments" %}

{% endif %} {% endblock %} @@ -105,15 +135,13 @@

{% trans "Muscles trained" %}

-
+
-
+
@@ -129,16 +157,16 @@
@@ -153,7 +181,8 @@ @@ -215,7 +244,8 @@ {% block options %}
- @@ -228,28 +258,28 @@
  • - - {% trans "View weight log" %} + + {% trans "View weight log" %}
  • {% if is_owner %}
  • - - {% trans "Edit workout" %} + + {% trans "Edit workout" %}
  • {% endif %}
  • - - {% trans "Export calendar file" %} + + {% trans "Export calendar file" %}
  • - - {% trans "Make a copy of this workout" %} + + {% trans "Make a copy of this workout" %}
  • {% if is_owner %} @@ -284,4 +314,4 @@
    {% endif %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/wger/manager/tests/test_workout.py b/wger/manager/tests/test_workout.py index bc6b7be6..0efe6770 100644 --- a/wger/manager/tests/test_workout.py +++ b/wger/manager/tests/test_workout.py @@ -145,7 +145,7 @@ class EditWorkoutTestCase(WorkoutManagerEditTestCase): pk = 3 user_success = 'test' user_fail = 'admin' - data = {'comment': 'A new comment'} + data = {'comment': 'A new comment', 'cycle_type': 'Microcycle'} class WorkoutOverviewTestCase(WorkoutManagerTestCase): diff --git a/wger/manager/views/day.py b/wger/manager/views/day.py index 5fd6de6f..5ad2eca7 100644 --- a/wger/manager/views/day.py +++ b/wger/manager/views/day.py @@ -21,7 +21,6 @@ from django.shortcuts import get_object_or_404 from django.http import HttpResponseRedirect from django.urls import reverse -from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext as _ from django.contrib.auth.decorators import login_required from django.views.generic import CreateView @@ -87,7 +86,19 @@ class DayEditView(DayView, UpdateView): # Send some additional data to the template def get_context_data(self, **kwargs): context = super(DayEditView, self).get_context_data(**kwargs) + workout = Day.objects.get(pk=self.kwargs['pk']).training + # Define context based on workout cycle type + if 'Microcycle' in workout.cycle_type: + context['form'].fields['day'].label = 'Day' + context['form'].fields['day'].queryset = DaysOfWeek.objects.filter(workout_plan='day') + elif 'Mesocycle' in workout.cycle_type: + context['form'].fields['day'].label = 'Week' + context['form'].fields['day'].queryset = DaysOfWeek.objects.filter(workout_plan='week') + elif 'Macrocycle' in workout.cycle_type: + context['form'].fields['day'].label = 'Month' + context['form'].fields['day'].queryset = DaysOfWeek.objects.filter(workout_plan='Month') context['title'] = _(u'Edit {0}').format(self.object) + return context @@ -96,7 +107,6 @@ class DayCreateView(DayView, CreateView): Generic view to add a new exercise day ''' - title = ugettext_lazy('Add workout day') owner_object = {'pk': 'workout_pk', 'class': Workout} def form_valid(self, form): @@ -108,9 +118,45 @@ def form_valid(self, form): # Send some additional data to the template def get_context_data(self, **kwargs): + workout = Workout.objects.get(pk=self.kwargs['workout_pk']) + if 'Microcycle' in workout.cycle_type: + DayCreateView.title = _('Add workout day') + elif 'Mesocycle' in workout.cycle_type: + DayCreateView.title = _('Add workout week') + elif 'Macrocycle' in workout.cycle_type: + DayCreateView.title = _('Add workout month') context = super(DayCreateView, self).get_context_data(**kwargs) + + cycle_type_selected = [] + if workout.canonical_representation['day_list']: + for canonical_representation in workout.canonical_representation['day_list']: + for option in canonical_representation['days_of_week']['day_list']: + cycle_type_selected.append(option.day_of_week) + + if 'Microcycle' in workout.cycle_type: + context['form'].fields['day'].label = 'Day' + context['form'].fields['description'].help_text = \ + _('A description of what is done on this day (e.g. "Pull day") ' + ' what body parts are trained (e.g. "Arms and abs")') + context['form'].fields['day'].queryset = DaysOfWeek.objects.filter( + workout_plan='day').exclude(day_of_week__in=cycle_type_selected) + elif 'Mesocycle' in workout.cycle_type: + context['form'].fields['description'].help_text = \ + _('A description of what is done on this week (e.g. ' + '"Pull week") or what body parts are trained (e.g. "Arms and abs")') + context['form'].fields['day'].label = 'Week' + context['form'].fields['day'].queryset = DaysOfWeek.objects.filter( + workout_plan='week').exclude(day_of_week__in=cycle_type_selected) + elif 'Macrocycle' in workout.cycle_type: + context['form'].fields['description'].help_text = \ + _('A description of what is done on this month (e.g. ' + '"Pull month") or what body parts are trained (e.g. "Arms and abs")') + context['form'].fields['day'].label = 'Month' + context['form'].fields['day'].queryset = DaysOfWeek.objects.filter( + workout_plan='Month').exclude(day_of_week__in=cycle_type_selected) context['form_action'] = reverse('manager:day:add', kwargs={'workout_pk': self.kwargs['workout_pk']}) + return context