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 %}
- |
-
+ {{ 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 @@
-
+
\ 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" %}
- |
-
+
+
+ {% 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 %}
+
+
{% endif %}
{% endblock %}
@@ -105,15 +135,13 @@ {% trans "Muscles trained" %}
-
@@ -129,16 +157,16 @@
{% trans "Export calendar file" %}
{% trans "Export this workout as a calendar file." %}
{% blocktrans %}You can then import the file it into your calendar
-application for example google calendar, outlook or iCal. This will create
-an appointment for each training day with the appropriate exercises.{% endblocktrans %}
+ application for example google calendar, outlook or iCal. This will create
+ an appointment for each training day with the appropriate exercises.{% endblocktrans %}
-
- {% trans "Export calendar file" %}
-
+
+ {% trans "Export calendar file" %}
+
@@ -153,7 +181,8 @@ {% trans "Download as PDF" %}
@@ -215,7 +244,8 @@ {% trans "Download as PDF" %}
{% block options %}
{% 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